【Codeforces】Orz Panda Cup
大大出的题
大大经常吐槽没有人补,所以我决定做一个
A. APA of Orz Pandas
题意:给你一个包含+-*/%和()的表达式,让你把它转化成java里BigInteger的形式
大概就像这样 "a.add(b).remainder(M).multiply(d.substract(e.multiply(f)).add(g.divide(h))).multiply (BigInteger.ValueOf(233)) ... ..."
输入的串长度<=1000
有意思的模拟题,不是很好写
首先要看清题,a+(b+c)应该输出为a.add(b.add(c))而不是a.add(b).add(c)
我之前看错题,使得这个题难度高了一点,但是那样也可以做,只需预处理无用括号即可
观察可以发现,不存在(a.add(b)).add(c)这种说法,也就是说,括号的本质作用是改变运算顺序,然后把括号里的表达式作为一个对象参与运算
那么就可以把括号里的东西看成一个变量,然后问题就转化为没有括号的表达式的处理
对于括号中的括号,可以把输出写成参数l和r的函数,功能为输出下标为[l,r]的子区间,然后递归处理
接下来的问题是找到输出序列中括号的包含范围,例如a+b*c要表示为a.add(b.multiply(c)),如何找到add右括号的位置
可以开一个栈
左括号可以直接压入
当压入右括号时,可以把左右括号中的所有运算符,包括括号全部湮灭
当压入+-时,可以把所有栈头的运算符湮灭,并令其右括号的位置在此运算符之前,但应在遇到左括号时停止
当压入*/%时,可以把栈头的*/%运算符湮灭,并令其右括号的位置在此运算符之前,但应遇到左括号或+-为止
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 char fc[5][10]={ 8 {'a','d','d',0}, 9 {'s','u','b','s','t','r','a','c','t',0}, 10 {'m','u','l','t','i','p','l','y',0}, 11 {'d','i','v','i','d','e',0}, 12 {'r','e','m','a','i','n','d','e','r',0} 13 }; 14 int fs[256]; 15 char s[11000]; int n; 16 char q[11000]; int p[11000],hd=0; 17 bool flg[11000]; 18 int nxt[11000]; 19 bool chck(char x){ return x=='+'||x=='-'||x=='*'||x=='/'||x=='%'||x=='('||x==')';} 20 bool chck1(char x){ return x=='+'||x=='-'||!x;} 21 bool chck2(char x){ return x=='*'||x=='/'||x=='%'||!x;} 22 void ot(int x,int y){ 23 for(int i=x;i<=y;++i)if(s[i] && s[i]!='(' && s[i]!=')'){ 24 if(!chck(s[i])) printf("%c",s[i]); 25 else{ 26 printf("."); 27 for(int j=0;fc[fs[(int)s[i]]][j];++j) printf("%c",fc[fs[(int)s[i]]][j]); 28 printf("("); ot(i+1,nxt[i]); printf(")"); 29 i=nxt[i]; 30 } 31 } 32 } 33 void prvs(){ 34 fs['+']=0,fs['-']=1,fs['*']=2,fs['/']=3,fs['%']=4; 35 hd=0; 36 for(int i=1;i<=n;++i) flg[i]=false; 37 s[0]=0,s[n+1]=0; 38 for(int i=1;i<=n;++i) nxt[i]=0; 39 } 40 int main(){ 41 //freopen("ddd.in","r",stdin); 42 while(scanf("%s",s+1)!=EOF){ 43 n=strlen(s+1); prvs(); 44 for(int i=1;i<=n;++i)if(chck(s[i])){ 45 if(s[i]==')'){ 46 bool mk1=false,mk2=false; 47 for(;q[hd]!='(';--hd){ 48 if(q[hd]=='+'||q[hd]=='-') mk1=true; 49 if(q[hd]=='*'||q[hd]=='/'||q[hd]=='%') mk2=true; 50 } 51 if((chck2(s[i+1])||chck2(s[p[hd]-1])) && !mk1){ 52 if(!mk2) flg[i]=true,flg[p[hd]]=true; 53 if(s[p[hd]-1]!='/'&&s[p[hd]-1]!='%') flg[i]=true,flg[p[hd]]=true; 54 } 55 if(s[i+1]==')' && s[p[hd]-1]=='(') 56 flg[i]=true,flg[p[hd]]=true; 57 if(chck1(s[i+1]) && chck1(s[p[hd]-1])) 58 flg[i]=true,flg[p[hd]]=true; 59 nxt[p[hd]]=i; 60 --hd; 61 } 62 else q[++hd]=s[i]; p[hd]=i; 63 } 64 hd=0; 65 for(int i=1;i<=n;++i){ 66 //if(flg[i]) s[i]=0; 67 if(s[i] && chck(s[i])){ 68 if(s[i]=='(') q[++hd]=s[i],p[hd]=i; 69 if(s[i]==')'){ 70 for(;q[hd]!='(';--hd) nxt[p[hd]]=i-1; 71 --hd; 72 } 73 if(chck1(s[i])){ 74 for(;hd && q[hd]!='(';--hd) nxt[p[hd]]=i-1; 75 q[++hd]=s[i],p[hd]=i; 76 } 77 if(chck2(s[i])){ 78 for(;hd && q[hd]!='(' && !chck1(q[hd]);--hd) nxt[p[hd]]=i-1; 79 q[++hd]=s[i],p[hd]=i; 80 } 81 } 82 } 83 for(;hd;--hd) nxt[p[hd]]=n; 84 /*for(int i=1;i<=n;++i) cout<<i<<" "; 85 cout<<endl; 86 for(int i=1;i<=n;++i) cout<<s[i]<<" "; 87 cout<<endl; 88 for(int i=1;i<=n;++i) cout<<nxt[i]<<" "; 89 cout<<endl;*/ 90 ot(1,n); printf("\n"); 91 } 92 return 0; 93 } 94 95
B. Brute Force of Orz Pandas
题意:
给你一个输出n层汉诺塔最优解的详细步骤的程序
输入n和k,要求你输入当此程序输入为n时的第k行输入,如果k大于总行数则输出Orz
n,k<=10^18
最开始的思路时数学/推公式/找规律解决,这样其实比较难搞
我们发现,直接运行给出的程序不可行是因为给出的程序会遍历每一种情况,而如果我们发现调用某个递归函数导致的输出大于当前查询的行数,完全可以跳过它
而对于每个递归的输出行数,可以由公式快速得到
那么类似在平衡树上查找第k大的方式
对于每一层,如果k小于第一个递归将输出的行数,那么进第一个递归,递归解决问题
如果等于第一个递归输出行数+1,说明找到我们的答案
如果大于第一个递归输出行数+1,就令k-=第一个递归输出行数+1,然后进第二个递归
对于n很大的情况,由于k不大,所有只有当函数中的size很小的时候才有可能涉及到中间的printf和第二个递归
那么可以预处理出最大的满足第一个递归输出行数>=所求行数的size,然后从这里开始递归即可
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 #define LL long long 8 LL n,m; 9 LL pw2[63]; 10 void gkd(int x,char l,char md,char r){ 11 if(pw2[x-1]-1>=m) gkd(x-1,l,r,md); 12 else if(pw2[x-1]-1==m-1) 13 printf("move %d from %c to %c\n",x,l,md); 14 else{ 15 m-=pw2[x-1]; 16 gkd(x-1,r,md,l); 17 } 18 } 19 int main(){ 20 //freopen("ddd.in","r",stdin); 21 pw2[0]=1; 22 for(int i=1;i<63;++i) pw2[i]=pw2[i-1]*2; 23 while(scanf("%lld%lld",&n,&m)!=EOF){ 24 char l='A',md='B',r='C'; 25 int tmp=1; 26 for(;pw2[tmp]<=m;++tmp); 27 if((n-tmp)&1) swap(md,r); 28 if(n<=60 && m>pw2[n]-1) printf("Orz\n"); 29 //注意n 30 else gkd(tmp,l,md,r); 31 } 32 return 0; 33 } 34 35
D. Director of Orz Pandas
题意:
给你两组物品,数量分别为n和m,每个物品有一个价值,还有k对关系,表示选了第一组的i物品就不能选第二组的j物品
现在问你可以取到的最大价值是多少
n,m<=100,k<=10000
可以把问题转化为先全部都选,然后剔除某些物品使得满足题目要求
裸的最小点权独立集,建图跑dinic即可,最坏复杂度O(n^2m)T不了
源到第一组的所有物品连边,权值为物品价值,第二组的所有物品到汇连边,权值为物品价值,冲突物品之间连无穷权值
鸽掉源或汇的一条边就表示剔除某个物品,只要有两个冲突的物品没被鸽掉就存在从源到汇的路径
所以最小鸽就是答案
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 int rd(){int z=0,mk=1; char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();} 9 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 10 return z*mk; 11 } 12 const int oo=1000000007; 13 struct edg{int y,nxt,v;}e[41000]; int lk[210],ltp=1; 14 void ist(int x,int y,int v){ 15 e[++ltp]=(edg){y,lk[x],v}; lk[x]=ltp; 16 e[++ltp]=(edg){x,lk[y],0}; lk[y]=ltp; 17 } 18 int n,m,o,a[210]; int s,t; 19 int lvl[210]; 20 int q[210],hd=0; 21 int crt[210]; 22 bool gtlvl(){ 23 for(int i=s;i<=t;++i) lvl[i]=0; 24 lvl[(q[(hd=1)]=s)]=1; 25 for(int k=1;k<=hd;++k){ 26 crt[q[k]]=lk[q[k]]; 27 for(int i=lk[q[k]];i;i=e[i].nxt)if(e[i].v && !lvl[e[i].y]){ 28 lvl[e[i].y]=lvl[q[k]]+1; 29 q[++hd]=e[i].y; 30 } 31 } 32 return lvl[t]; 33 } 34 int mxflw(int x,int y){ 35 if(x==t) return y; 36 int bwl=0,flw=0; 37 for(int i=crt[x];i && bwl<y;i=e[i].nxt)if(lvl[e[i].y]==lvl[x]+1 && e[i].v) 38 if((flw=mxflw(e[i].y,min(e[i].v,y-bwl)))){ 39 e[i].v-=flw,e[i^1].v+=flw; 40 //cout<<e[i^1].y<<"->"<<e[i].y<<" "<<flw<<endl; 41 bwl+=flw; 42 if(!e[i].v) crt[x]=e[i].nxt; 43 } 44 if(!bwl) lvl[x]=0; 45 return bwl; 46 } 47 int dnc(){ 48 int bwl=0,flw=0; 49 while(gtlvl())while((flw=mxflw(s,oo))){ 50 bwl+=flw; 51 //printf("%d %d\n",bwl,flw); 52 } 53 return bwl; 54 } 55 void prvs(){ 56 ltp=1; 57 for(int i=0;i<=n+m+1;++i) lk[i]=0; 58 } 59 int main(){ 60 //freopen("ddd.in","r",stdin); 61 while(scanf("%d%d",&n,&m)!=EOF){ 62 prvs(); s=0,t=n+m+1; 63 int sm=0; 64 for(int i=1;i<=n;++i){ 65 a[i]=rd(); 66 sm+=a[i]; 67 ist(s,i,a[i]); 68 } 69 for(int i=n+1;i<=n+m;++i){ 70 a[i]=rd(); 71 sm+=a[i]; 72 ist(i,t,a[i]); 73 } 74 o=rd(); 75 int l,r; 76 while(o --> 0){ 77 l=rd(),r=rd(); 78 if(l>r) swap(l,r); 79 ist(l,r,oo); 80 } 81 printf("%d\n",sm-dnc()); 82 /*for(int i=s;i<=t;++i){ 83 cout<<i<<":"<<endl; 84 for(int j=lk[i];j;j=e[j].nxt) printf("%d %d\n",e[j].y,e[j].v); 85 }*/ 86 } 87 return 0; 88 } 89 90
H. Horton and Orz Pandas
题意:
给你一个n个点m条边的无向图,每条边都有两个权值a和b,现在问你在所有的包含所有点的联通子图中,a权值的和除以b权值的和的最大值是多少
n<=1000,m<=1e7
给a/b排序,然后Kruskal求最小生成树是错的
原因一,尽管有c/d<(a+c)/(b+d)<a/b当c/d<a/b的结论,但是
这也是最优比率生成树不能直接排序做的原因
原因二,答案不是最优比率生成树(即a权值的和除以b权值的和最大的生成树)
因为c/d<(a+c)/(b+d)<a/b,所以如果有树边的比率小于答案,那么把这条边加入是可以让答案增加的,而题中并没有规定一定要求生成树
本题实质上是求包含所有点的最大密度子图,是经典的01分数规划问题
令原图为S,λ=a(x)/b(x),其中a(x)表示子图x的a权之和,λ*=a(x*)/b(x*)为λ的最优值,则有0=a(x*)-λb(x*)
不妨设g(λ)=max{x⊆S | a(x)-λb(x)},则g(λ)是单调递减函数
证明:
对于λ1>λ2,g(λ1)=a(x1)-λ1b(x1) < a(x1)-λ2b(x1) (b(x)>0),而x1未必是λ2的最优解,即g(λ2)>=a(x1)-λ2b(x1)
故g(λ1)<=g(λ2)
又可以发现g(λ*)=0
证明:
若g(λ)=max{x⊆S | a(x)-λ*b(x)}>0,即∃x1使a(x1)-λ*b(x1)>0,则有λ*<a(x1)/b(x1),与λ*的定义不符
而根据定义,,∃x*使a(x1)-λ*b(x1)=0,故g(λ*)=0
那么只需二分答案,然后根据二分的答案构造新的边权a-bλ
此时原来的分数规划问题就转化为线性的问题,可以直接用Kruskal求最大生成树得到最优比率生成树
本题要求求包含所有点的最大密度子图
那么可以考虑对于只有一个边权的包含所有点的最大权值子图如何求
显然只需在最大生成树的基础上,把所有非负边权加入即可
那么对于构造出来的新边权做如上处理,检查答案是否大于零
本题注意:
1.浮点数二分不能(l+eps<r),会因为精度问题死循环,而应该计算好循环次数后直接for
2.因为sort次数很多,而结构体sort可能很慢,所以可以把结构体sort的比较函数的参数写成传地址形式,即参数名前加'&',或者手写排序
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<time.h> 7 using namespace std; 8 const double eps=1e-9; 9 int rd(){int z=0,mk=1; char ch=getchar(); 10 while(ch<'0'||ch>'9'){if(ch=='-') mk=-1; ch=getchar();} 11 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 12 return z*mk; 13 } 14 struct nds{int x,y,a,b; double c;}b[110000]; 15 int n,m; 16 int tp[11000]; 17 int gttp(int x){ 18 int y=x,tmp; 19 while(tp[y]!=y) y=tp[y]; 20 while(x!=y){ 21 tmp=tp[x]; 22 tp[x]=y; 23 x=tmp; 24 } 25 return y; 26 } 27 inline void mg(int x,int y){ tp[gttp(x)]=gttp(y);} 28 //用于求最优比率生成树的二分上下界 29 double gtmn(){ 30 for(int i=1;i<=n;++i) tp[i]=i; 31 double aa=0,ab=0; 32 for(int i=1;i<=m;++i)if(gttp(b[i].x)!=gttp(b[i].y)){ 33 mg(b[i].x,b[i].y); 34 aa+=b[i].a,ab+=b[i].b; 35 } 36 return ab/aa; 37 } 38 //bool cmp1(nds x,nds y){ return x.a<y.a;} 39 //bool cmp2(nds x,nds y){ return x.b>y.b;} 40 double gtmx(){ 41 double aa=0,ab=0; 42 /*sort(b+1,b+m+1,cmp1); 43 for(int i=1;i<n;++i) aa+=b[i].a; 44 sort(b+1,b+m+1,cmp2);*/ 45 for(int i=1;i<n;++i) ab+=b[i].b; 46 //return ab/aa; 47 return ab; 48 } 49 int s; //用于检查运行时间 50 //手写堆排序 51 nds a[110000]; 52 void st(){ 53 for(int i=1;i<=m;++i) 54 for(int j=i;j>1 && b[j].c<b[j>>1].c;j>>=1) swap(b[j],b[j>>1]); 55 int sz=m; 56 for(int i=1;i<=m;++i){ 57 a[i]=b[1]; 58 swap(b[1],b[sz--]); 59 for(int j=1;j<=sz;){ 60 int k=j; 61 if((j<<1)<=sz && b[j<<1].c<b[k].c) k=(j<<1); 62 if((j<<1|1)<=sz && b[j<<1|1].c<b[k].c) k=(j<<1|1); 63 if(k==j) break; 64 swap(b[j],b[k]); 65 j=k; 66 } 67 } 68 for(int i=1;i<=m;++i) b[i]=a[i]; 69 } 70 bool cmp(nds &x,nds &y){ return x.c<y.c;} //使结构体sort加速 71 bool chck(double x){ 72 for(int i=1;i<=m;++i) b[i].c=b[i].b-x*b[i].a; 73 sort(b+1,b+m+1,cmp); 74 //st(); 75 //cout<<clock()-s<<endl; 76 //s=clock(); 77 double bwl=0; 78 for(int i=1;i<=n;++i) tp[i]=i; 79 for(int i=m;i>=1;--i){ 80 if(gttp(b[i].x)!=gttp(b[i].y)){ 81 mg(b[i].x,b[i].y); 82 bwl+=b[i].c; 83 } 84 else if(b[i].c>0) bwl+=b[i].c; 85 } 86 //cout<<clock()-s<<endl; 87 //s=clock(); 88 return bwl>0; 89 } 90 double bnrsch(){ 91 double l=gtmn(),r=gtmx(),md; 92 //while(l+eps<r){ 注意浮点数二分的写法 93 int tmp=(log(r-l+1)+10*log(10))/log(2); 94 for(int i=1;i<=tmp;++i){ 95 md=(l+r)/2; 96 (chck(md) ? l : r)=md; 97 //cout<<i<<" "<<clock()-s<<endl; 98 } 99 return chck(r) ? r : l; 100 } 101 int main(){ 102 //freopen("ddd.in","r",stdin); 103 //s=clock(); 104 while(scanf("%d%d",&n,&m)!=EOF){ 105 for(int i=1;i<=m;++i) b[i].x=rd(),b[i].y=rd(),b[i].a=rd(),b[i].b=rd(); 106 printf("%.10lf\n",bnrsch()); 107 } 108 //cout<<clock()-s<<endl; 109 return 0; 110 }