科林明伦杯 哈尔滨理工大学第十届程序设计竞赛 题解
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 1000005
#define INF 0x3f3f3f3f
int head[N],dp[N];
int tot,ans;
struct
{
int to,next,v;
} edge[N*2];
void addedge(int from,int to,int v)
{
edge[tot].to=to;
edge[tot].next=head[from];
edge[tot].v=v;
head[from]=tot++;
}
void dfs(int x,int pre)
{
int ma[2];
ma[0]=dp[x];
ma[1]=-INF;
int zt=0;
for(int i=head[x]; i!=-1; i=edge[i].next)
{
if(edge[i].to==pre)
continue;
dfs(edge[i].to,x);
ma[1]=max(ma[1],dp[edge[i].to]+edge[i].v);
if(ma[0]<ma[1])
swap(ma[0],ma[1]);
zt=1;
}
if(zt)
ans=max(ans,ma[0]+ma[1]);
dp[x]=ma[0];
}
int main()
{
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
tot=0;
ans=-INF;
memset(head,-1,sizeof(head));
int n;
scanf("%d",&n);
int fa,v;
for(int i=2; i<=n; i++)
{
scanf("%d %d",&fa,&v);
addedge(fa,i,v);
addedge(i,fa,v);
}
for(int i=1; i<=n; i++)
{
scanf("%d",&dp[i]);
}
dfs(1,-1);
printf("%d\n",ans);
}
}
#include<stdio.h>
#define N 100005
long long f[N],a[N];
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
long long t;
scanf("%lld",&t);
long long num=0;
while(t--)
{
long long n;
scanf("%lld",&n);
num+=n;
long long sum=0;
for(long long i=0;i<n;i++)
{
scanf("%lld",&a[i]);
if(i)
f[i]=a[i]-a[i-1];
else
f[i]=a[i];
if(f[i]>0)
sum+=f[i];
}
printf("%lld\n",sum-1);
}
}
面积
#include<stdio.h>
#include<algorithm>
double pi=3.14;
int main()
{
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
double x;
scanf("%lf",&x);
double ans = 1.0*x*x+2.0*pi*(x/2.0)*(x/2.0);
printf("%.2f\n",ans);
}
}
扔硬币
思路:由于已知部分硬币的方向。因此,扔n个硬币的情况要在2的n次幂的中去掉少于m个硬币是反面的情况。
对于k+m>n是不可能的,直接输出0.
其余情况:C(n,k)/[ 2^n - ∑(C(n,i)) ] (i<m)
#include<stdio.h>
#define N 100005
#define M 1000000007
long long f[N],finv[N],inv[N];
long long cnm(long long n,long long m)
{
return ((f[n]*finv[m]%M)*finv[n-m])%M;
}
long long km(long long x,long long y)
{
long long sum=1;
while(y)
{
if(y&1)
sum=(sum*x)%M;
y/=2;
x=(x*x)%M;
}
return sum;
}
void init()
{
f[0]=inv[0]=finv[0]=1;
f[1]=inv[1]=finv[1]=1;
for(long long i=2;i<=100000;i++)
{
f[i]=f[i-1]*i%M;
inv[i]=(M-M/i)*inv[M%i]%M;
finv[i]=(finv[i-1]*inv[i])%M;
}
}
int main()
{
//freopen("C.in.txt","r",stdin);
//freopen("C.out.txt","w",stdout);
init();
int t;
scanf("%d",&t);
while(t--)
{
long long n,m,k;
scanf("%lld %lld %lld",&n,&m,&k);
if(m+k>n)
printf("0\n");
else
{
long long sum=0;
for(long long i=0; i<m; i++)
sum=(sum+cnm(n,i))%M;
sum=(km(2,n)-sum+M)%M;
sum=cnm(n,k)*km(sum,M-2)%M;
printf("%lld\n",sum);
}
}
}
/*
1
3 1 1
428571432
3/(2^3-1)
*/
赛马
思路:
每次暴力寻找比b[i]大且没参赛过的最小a[j]值(1<=i<=n,1<=j<=n)。时间复杂度O(n^2)。
#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 1005
int a[N],b[N];
bool vis[N];
struct s
{
int tp,v;
}f[N*2];
bool cmp(s aa,s bb)
{
if(aa.v==bb.v)
{
return aa.tp<bb.tp;
}
return aa.v<bb.v;
}
int main()
{
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
// n^2
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
vis[i]=true;
}
sort(a+1,a+n+1);
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
int zt=1,id=-1;
for(int j=1;j<=n;j++)
{
if(a[j]>b[i]&&vis[j])
{
zt=0;
vis[j]=false;
ans++;
break;
}
else if(vis[j]&&id==-1)
{
id=j;
}
}
if(zt)
{
vis[id]=false;
}
}
printf("%d\n",ans);
// nlogn
// for(int i=1;i<=n;i++)
// {
// f[i].tp=1;
// scanf("%d",&f[i].v);
// }
// for(int i=1;i<=n;i++)
// {
// f[n+i].tp=2;
// scanf("%d",&f[n+i].v);
// }
// sort(f+1,f+n*2+1,cmp);
// int num=0,ans=0;
// for(int i=1;i<=2*n;i++)
// {
// if(f[i].tp==2)
// num++;
// else if(num>0)
// {
// num--;
// ans++;
// }
// }
// printf("%d\n",ans);
}
}
三角形
思路:三角形两边之和大于第三边,因此不构成三角形的条件就是存在两边之和不超过另一边。所以按照斐波那契数列的方法切割可以使得切割的段数最大,1,1,2,3,5这样可以使得任意三根木棒不能组成三角形,最后无法切割的部分组成一根长的木棒。
#include <bits/stdc++.h>
using namespace std;
unsigned long long a[105];
unsigned long long b[105];
int main()
{
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
a[1] = 1; a[2] = 2;
unsigned long long b = 1, c = 1;
for(int i = 3; i <= 91; ++i) {
a[i] = a[i - 1] + b + c;
b = c; c = a[i] - a[i - 1];
// printf("%llu\n", a[i]);
}
int T;
scanf("%d", &T);
while(T--) {
unsigned long long n;
scanf("%llu", &n);
if (n == 0) {
printf("0\n");
continue;
}
int ans = 0;
for(int i = 1; i <= 91; ++i) {
if(a[i] <= n) ans = i;
}
printf("%d\n", ans);
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
养花
思路:采用网络流直接进行建图,对于四种操作每次都新建两个点from和to,题面中的[a1,a2]区间每个节点连接到from节点,to连接[b1,b2]区间的每个节点。然后跑最大流即可。
但对于区间的最大流还可采用线段树建图的方式,建立两棵线段树,每种操作新建节点时直接在两棵线段树上所对应的区间即可。
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
struct Edge{
int next,to,f;
};
struct Dinic
{
int s,t;
Edge e[4000010];
int cnt=2,head[4000010]={0};
int dis[4000010]={0};
Dinic (){}
void init(int _s,int _t)
{
cnt=2;
s=_s,t=_t;
memset(head,0,sizeof(head));
}
void addedge(int u,int v,int f)
{
e[cnt]={head[u],v,f};
head[u]=cnt++;
e[cnt]={head[v],u,0};
head[v]=cnt++;
}
bool bfs()
{
memset(dis,0,sizeof(dis));
dis[s]=1;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(!dis[v]&&e[i].f>0)
{
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[t]!=0;
}
int dfs(int u,int flow)
{
if(u==t||flow==0) return flow;
int flow_sum=0;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to,f=e[i].f;
if(dis[v]!=dis[u]+1||f==0) continue;
int temp=dfs(v,min(flow-flow_sum,f));
e[i].f-=temp;
e[i^1].f+=temp;
flow_sum+=temp;
if(flow_sum>=flow) break;
}
if(!flow_sum) dis[u]=-1;
return flow_sum;
}
int maxflow()
{
int ans=0;
while(bfs())
{
while(int temp=dfs(s,0x7fffffff)) {
ans+=temp;
}
}
return ans;
}
}DC;
struct Node {
int l, r;
Node(): l(0), r(0) {}
}T[4000010];
int tot, rt1, rt2;
int n, m, k;
int h[10007];
void build(int &r1, int &r2, int l, int r)
{
r1 = ++tot;
r2 = (l == r ? r1: ++tot);
if(l == r) {
// if(l == 1) DC.addedge(DC.s, r1, n);
if(l == k) DC.addedge(r1, DC.t, n);
else DC.addedge(DC.s, r1, h[l]);
return ;
}
int mid = (l + r) >> 1;
build(T[r1].l, T[r2].l, l, mid);
build(T[r1].r, T[r2].r, mid + 1, r);
DC.addedge(r1, T[r1].l, INF);
DC.addedge(r1, T[r1].r, INF);
DC.addedge(T[r2].l, r2, INF);
DC.addedge(T[r2].r, r2, INF);
}
void query(int rt, int l, int r, int L, int R, int p, int id)
{
if(!rt || l > R || r < L) return;
if(L <= l && r <= R) {
if (id == 0) DC.addedge(p, rt, INF);
else DC.addedge(rt, p, INF);
return ;
}
int mid = (l + r) >> 1;
query(T[rt].l, l, mid, L, R, p, id);
query(T[rt].r, mid + 1, r, L, R, p, id);
}
int main()
{
freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d%d", &n, &m, &k);
DC.init(1, 2);
tot = 2;
rt1 = rt2 = 0;
for (int i = 1; i <= k; ++i)
h[i] = 0;
for (int i = 1; i <= n; ++i) {
int as;
scanf("%d", &as);
h[as]++;
}
build(rt1, rt2, 1, k);
for(int i = 1; i <= m; ++i) {
int op, num;
scanf("%d%d", &op, &num);
int l1, l2, r1, r2;
if (op == 1) {
scanf("%d%d", &l1, &l2);
r1 = l1; r2 = l2;
}
if (op == 2) {
scanf("%d%d%d", &l1, &r1, &l2);
r2 = l2;
}
if (op == 3) {
scanf("%d%d%d", &l1, &l2, &r2);
r1 = l1;
}
if (op == 4) {
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
}
if(l1<1||l2<1||r1<1||r2<1||l1>k||l2>k||r1>k||r2>k)
printf("NO\n");
int from = ++tot;
int to = ++tot;
DC.addedge(from, to, num);
query(rt1, 1, k, l2, r2, to, 0);
query(rt2, 1, k, l1, r1, from, 1);
}
// printf("%d\n", DC.maxflow());
}
fclose(stdin);
fclose(stdout);
return 0;
}
/*
5 4 5
1 1 1 1 1
1 3 1 3
1 3 3 2
1 3 2 5
4 1 1 1 4 5
*/
直线
思路:平面中直线交点最多n*(n-1)/2个,需要大数乘法。因为公式中(n-1)和除2操作可以直接在unsigned long long中完成,只需要采用大数乘,或其他方法。
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
#define MAXN 9999
#define MAXSIZE 1010
#define DLEN 4
class BigNum
{
private:
int a[500]; //可以控制大数的位数
int len;
public:
BigNum(){len=1;memset(a,0,sizeof(a));} //构造函数
BigNum(const int); //将一个int类型的变量转化成大数
BigNum(const char*); //将一个字符串类型的变量转化为大数
BigNum(const BigNum &); //拷贝构造函数
BigNum &operator=(const BigNum &); //重载赋值运算符,大数之间进行赋值运算
friend istream& operator>>(istream&,BigNum&); //重载输入运算符
friend ostream& operator<<(ostream&,BigNum&); //重载输出运算符
BigNum operator+(const BigNum &)const; //重载加法运算符,两个大数之间的相加运算
BigNum operator-(const BigNum &)const; //重载减法运算符,两个大数之间的相减运算
BigNum operator*(const BigNum &)const; //重载乘法运算符,两个大数之间的相乘运算
BigNum operator/(const int &)const; //重载除法运算符,大数对一个整数进行相除运算
BigNum operator^(const int &)const; //大数的n次方运算
int operator%(const int &)const; //大数对一个int类型的变量进行取模运算
bool operator>(const BigNum &T)const; //大数和另一个大数的大小比较
bool operator>(const int &t)const; //大数和一个int类型的变量的大小比较
void print(); //输出大数
};
BigNum::BigNum(const int b) //将一个int类型的变量转化为大数
{
int c,d=b;
len=0;
memset(a,0,sizeof(a));
while(d>MAXN)
{
c=d-(d/(MAXN+1))*(MAXN+1);
d=d/(MAXN+1);
a[len++]=c;
}
a[len++]=d;
}
BigNum::BigNum(const char *s) //将一个字符串类型的变量转化为大数
{
int t,k,index,L,i;
memset(a,0,sizeof(a));
L=strlen(s);
len=L/DLEN;
if(L%DLEN)len++;
index=0;
for(i=L-1;i>=0;i-=DLEN)
{
t=0;
k=i-DLEN+1;
if(k<0)k=0;
for(int j=k;j<=i;j++)
t=t*10+s[j]-'0';
a[index++]=t;
}
}
BigNum::BigNum(const BigNum &T):len(T.len) //拷贝构造函数
{
int i;
memset(a,0,sizeof(a));
for(i=0;i<len;i++)
a[i]=T.a[i];
}
BigNum & BigNum::operator=(const BigNum &n) //重载赋值运算符,大数之间赋值运算
{
int i;
len=n.len;
memset(a,0,sizeof(a));
for(i=0;i<len;i++)
a[i]=n.a[i];
return *this;
}
istream& operator>>(istream &in,BigNum &b)
{
char ch[MAXSIZE*4];
int i=-1;
in>>ch;
int L=strlen(ch);
int count=0,sum=0;
for(i=L-1;i>=0;)
{
sum=0;
int t=1;
for(int j=0;j<4&&i>=0;j++,i--,t*=10)
{
sum+=(ch[i]-'0')*t;
}
b.a[count]=sum;
count++;
}
b.len=count++;
return in;
}
ostream& operator<<(ostream& out,BigNum& b) //重载输出运算符
{
int i;
cout<<b.a[b.len-1];
for(i=b.len-2;i>=0;i--)
{
printf("%04d",b.a[i]);
}
return out;
}
BigNum BigNum::operator+(const BigNum &T)const //两个大数之间的相加运算
{
BigNum t(*this);
int i,big;
big=T.len>len?T.len:len;
for(i=0;i<big;i++)
{
t.a[i]+=T.a[i];
if(t.a[i]>MAXN)
{
t.a[i+1]++;
t.a[i]-=MAXN+1;
}
}
if(t.a[big]!=0)
t.len=big+1;
else t.len=big;
return t;
}
BigNum BigNum::operator-(const BigNum &T)const //两个大数之间的相减运算
{
int i,j,big;
bool flag;
BigNum t1,t2;
if(*this>T)
{
t1=*this;
t2=T;
flag=0;
}
else
{
t1=T;
t2=*this;
flag=1;
}
big=t1.len;
for(i=0;i<big;i++)
{
if(t1.a[i]<t2.a[i])
{
j=i+1;
while(t1.a[j]==0)
j++;
t1.a[j--]--;
while(j>i)
t1.a[j--]+=MAXN;
t1.a[i]+=MAXN+1-t2.a[i];
}
else t1.a[i]-=t2.a[i];
}
t1.len=big;
while(t1.a[len-1]==0 && t1.len>1)
{
t1.len--;
big--;
}
if(flag)
t1.a[big-1]=0-t1.a[big-1];
return t1;
}
BigNum BigNum::operator*(const BigNum &T)const //两个大数之间的相乘
{
BigNum ret;
int i,j,up;
int temp,temp1;
for(i=0;i<len;i++)
{
up=0;
for(j=0;j<T.len;j++)
{
temp=a[i]*T.a[j]+ret.a[i+j]+up;
if(temp>MAXN)
{
temp1=temp-temp/(MAXN+1)*(MAXN+1);
up=temp/(MAXN+1);
ret.a[i+j]=temp1;
}
else
{
up=0;
ret.a[i+j]=temp;
}
}
if(up!=0)
ret.a[i+j]=up;
}
ret.len=i+j;
while(ret.a[ret.len-1]==0 && ret.len>1)ret.len--;
return ret;
}
BigNum BigNum::operator/(const int &b)const //大数对一个整数进行相除运算
{
BigNum ret;
int i,down=0;
for(i=len-1;i>=0;i--)
{
ret.a[i]=(a[i]+down*(MAXN+1))/b;
down=a[i]+down*(MAXN+1)-ret.a[i]*b;
}
ret.len=len;
while(ret.a[ret.len-1]==0 && ret.len>1)
ret.len--;
return ret;
}
int BigNum::operator%(const int &b)const //大数对一个 int类型的变量进行取模
{
int i,d=0;
for(i=len-1;i>=0;i--)
d=((d*(MAXN+1))%b+a[i])%b;
return d;
}
BigNum BigNum::operator^(const int &n)const //大数的n次方运算
{
BigNum t,ret(1);
int i;
if(n<0)exit(-1);
if(n==0)return 1;
if(n==1)return *this;
int m=n;
while(m>1)
{
t=*this;
for(i=1;(i<<1)<=m;i<<=1)
t=t*t;
m-=i;
ret=ret*t;
if(m==1)ret=ret*(*this);
}
return ret;
}
bool BigNum::operator>(const BigNum &T)const //大数和另一个大数的大小比较
{
int ln;
if(len>T.len)return true;
else if(len==T.len)
{
ln=len-1;
while(a[ln]==T.a[ln]&&ln>=0)
ln--;
if(ln>=0 && a[ln]>T.a[ln])
return true;
else
return false;
}
else
return false;
}
bool BigNum::operator>(const int &t)const //大数和一个int类型的变量的大小比较
{
BigNum b(t);
return *this>b;
}
void BigNum::print() //输出大数
{
int i;
printf("%d",a[len-1]);
for(i=len-2;i>=0;i--)
printf("%04d",a[i]);
printf("\n");
}
int main()
{
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
int T;
scanf("%d", &T);
while(T--) {
string n;
cin >> n;
BigNum a(n.c_str());
a = a * (a - 1) / 2;
a.print();
}
// fclose(stdout);
// fclose(stdin);
return 0;
}
字典序
思路:对于a[i]位置的数据分一下三种情况进行讨论
若a[i] == a[i+1],那么删除a[i]后与删除a[i + 1]的结果一样,那么a[i]出处的字典序会比a[i+1]出小
若a[i] < a[i+1],那么删除a[i]后的数组在第i位上比删除a[i+1]所得到的更大,所以第i个位置的字典序比后面位置的字典序都小
若a[i] > a[i+1],那么删除a[i]后的数组在第i位上比删除a[i+1]所得到的更小,所以第i个位置的字典序比后面位置的字典序都大。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 7;
int a[maxn];
int L[maxn], R[maxn];
int cnt;
deque<int> d;
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
int T;
scanf("%d", &T);
while(T--) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
cnt = 0;
for (int i = 1; i <= n; ++i) {
int j = i;
while(a[j + 1] == a[j] && j < n) j++;
a[++cnt] = a[i]; L[cnt] = i; R[cnt] = j;
i = j;
}
a[cnt + 1] = 0;
d.clear();
for (int i = cnt; i > 0; --i) {
if(a[i] > a[i + 1]) d.push_front(i);
else d.push_back(i);
}
bool ok = true;
for(auto i : d) {
for (int j = L[i]; j <= R[i]; ++j) {
if(!ok) printf(" ");
printf("%d", j);
ok = false;
}
}
puts("");
}
fclose(stdin);
fclose(stdout);
return 0;
}
/*
3
5
699 713 941 972 451
5
2 3 4 5 1
2 4 5 3 1
*/
最大值
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
char a[maxn];
int Next[maxn];
int len;
void getNext()
{
int j=0,k=-1;
Next[0]=-1;
while(j<len){
if(k==-1||a[j]==a[k])Next[++j]=++k;
else k=Next[k];
}
}
int main()
{
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
int T;
scanf("%d", &T);
while(T--){
scanf("%s", a);
len = strlen(a);
getNext();
int ans = 0;
for (int i = 1; i <= len; ++i)
ans = max(ans, Next[i]);
printf("%d\n", ans);
}
// fclose(stdin);
// fclose(stdout);
return 0;
}


查看3道真题和解析