又是炸掉的一次考试
T1.方程的解
本次考试最容易骗分的一道题,但是由于T2花的时间太多,我竟然连a+b=c都没判。。暴力掉了40分。
首先a+b=c,只有一组解。
然后是a=1,b=1,答案是c-1,不解释。
对于最大的数据,我们可以用exgcd求出一组特解,之后的通解为x+(b/gcd)*k, y+(a/gcd)*k.
求出正整数解的个数即可。
注意有很多特判,慢慢调试就好(改这题的时间比我改T3的时间都长)
#include<bits/stdc++.h> #define m 65535 #define int long long using namespace std; int t,a,b,c,x,y; long long ans; int exgcd(int a,int b,int &x,int &y) { if(b==0) { x=1,y=0; return a; } int gcd=exgcd(b,a%b,y,x); y-=(a/b)*x; return gcd; } main() { scanf("%lld",&t); while(t--) { ans=0; scanf("%lld%lld%lld",&a,&b,&c); if(a+b==c&&a>=1&&b>=1) { puts("1"); continue; } if(a<0&&b<0&&c<0) a=-a,b=-b,c=-c; if(a==1&&b==1) { ans=c-1; if(ans>0&&ans<=65535) { printf("%lld\n",ans); continue; } if(ans<=0) puts("0"); if(ans>m) puts("ZenMeZheMeDuo"); continue; } if(t<=100&&a<=1000&&a>=1&&b<=1000&&b>=1&&c>=1&&c<=1000) { for(int i=1;i<=1000;i++) for(int j=1;j<=1000;j++) { if(a*i+b*j>c)break; if(a*i+b*j==c)ans++; } if(ans>m)puts("ZenMeZheMeDuo"); else printf("%lld\n",ans); continue; } if(a==0&&b==0) { if(c==0)puts("ZenMeZheMeDuo"); else puts("0"); continue; } if(a==0) { if(b>0) { if(c>0&&(!(c%b)))puts("ZenMeZheMeDuo"); else puts("0"); } if(b<0) { if(c<0&&(!(c%b)))puts("ZenMeZheMeDuo"); else puts("0"); } continue; } if(b==0) { if(a>0) { if(c>0&&(!(c%a)))puts("ZenMeZheMeDuo"); else puts("0"); } if(a<0) { if(c<0&&(!(c%a)))puts("ZenMeZheMeDuo"); else puts("0"); } continue; } if((a<0&&b<0&&c>=0)||(a>0&&b>0&&c<=0)) { puts("0"); continue; } if(a<0&&b<0&&c<0) a=-a,b=-b,c=-c; int gcd=exgcd(a,b,x,y); if(c%gcd) { puts("0"); continue; } if(a*b<0) { puts("ZenMeZheMeDuo"); continue; } int k=c/gcd,xx=b/gcd,yy=a/gcd; x*=k,y*=k; if(xx<0) xx=-xx,yy=-yy; if(x<=0) { int xxx=x/xx+1; x+=xxx*xx,y-=yy*xxx; } if(y<=0) { int xxx=y/yy+1; x-=xx*xxx,y+=yy*xxx; } while(x<=0) x+=xx,y-=yy; while(y<=0) x-=xx,y+=yy; if(!x||!y) { puts("0"); continue; } if(x/y<0||y/x<0) { puts("0"); continue; } int aa=x/xx+1,bb=y/yy+1; if(!(x%xx))aa--; if(!(y%yy))bb--; ans=max(aa,bb); if(ans>m)puts("ZenMeZheMeDuo"); else printf("%lld\n",ans); } return 0; }
T2.visit
本场考试最可惜的一道题,打出正解,却因为答案为0的特判wa(改一个字符,70分->AC)
考试的时候先打了个n^3dp,然后开始找规律
10,5,5 = C(10,5)*C(10,0);
10,4,4 = C(10,5)*C(10,1); 10,4,6=C(10,4)*C(10,0);
10,3,3 = C(10,5)*C(10,2); 10,3,5=C(10,4)*C(10,1); 10,3,7=C(10,3)*C(10,0);
10,2,2 = C(10,5)*C(10,3); 10,2,4=C(10,4)*C(10,2); 10,2,6=C(10,3)*C(10,1); 10,2,8=C(10,2)*C(10,0);
10,1,1 = C(10,5)*C(10,4);
10,0,0 = C(10,5)*C(10,5);
这个表我没有打完,但是我感觉已经很显然了,ans=C(t,(t-n-m)/2)*C(t,t/2-abs(n-m)/2);
然后我终于理解了数据范围最后,mod为若干质数乘积的意思。。。
恩,然后我就开始手推crt,码码码,大概一个小时调完了,和dp对了几个点,感觉没啥问题,没打对拍放心的去看T3了。
考后发现自己打了个这个:
if(((t%(n+m))&1)) { puts("0"); return 0; }
???????????我干了啥?把%改成-,AC。一个特判卡掉30分,我。。。。
代码可能有些难看,忍忍就好(考场上现推的crt,现码的lucas)
至于组合数的解释,请参考其他人的题解。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 int t,mod,n,m,kk,pp[50]; 5 long long ans,js[1000005],r[50]; 6 long long qpow(long long x,long long y,long long p) 7 { 8 long long ans=1; 9 while(y) 10 { 11 if(y&1)ans=ans*x%p; 12 x=x*x%p; 13 y>>=1; 14 } 15 return ans; 16 } 17 long long C(long long x,long long y,long long p) 18 { 19 js[0]=1; 20 for(int i=1;i<=x;i++) 21 { 22 js[i]=js[i-1]*i%p; 23 } 24 return js[x]*qpow(js[y],p-2,p)%p*qpow(js[x-y],p-2,p)%p; 25 } 26 long long crt() 27 { 28 long long mm=mod,ans=0; 29 for(int i=1;i<=kk;i++) 30 { 31 long long c=mm/pp[i]; 32 long long ni=qpow(c,pp[i]-2,pp[i]); 33 ans=(ans+r[i]*ni%mod*c%mod)%mod; 34 } 35 return ans; 36 } 37 long long lu(long long x,long long y,long long p) 38 { 39 long long ans=1; 40 while(x&&y) 41 { 42 long long a=x%p,b=y%p; 43 x/=p,y/=p; 44 if(a<b)return 0; 45 ans=ans*C(a,b,p)%p; 46 } 47 return ans%p; 48 } 49 bool check(long long x) 50 { 51 if(x==1)return false; 52 long long sq=sqrt(x); 53 for(int i=2;i<=sq;i++) 54 if(!(x%i)) 55 return false; 56 return true; 57 } 58 long long L(long long x,long long y) 59 { 60 for(int i=1;i<=kk;i++) 61 { 62 int k=pp[i]; 63 r[i]=lu(x,y,k); 64 } 65 return crt(); 66 } 67 main() 68 { 69 scanf("%lld%lld%lld%lld",&t,&mod,&n,&m); 70 int sq=sqrt(mod)+1; 71 for(int i=1;i<=sq;i++) 72 { 73 if(!(mod%i)) 74 { 75 if(check(i)) 76 { 77 pp[++kk]=i; 78 } 79 if((i!=mod/i)&&check(mod/i)) 80 { 81 pp[++kk]=mod/i; 82 } 83 } 84 } 85 if(n<0)n=-n; 86 if(m<0)m=-m; 87 if(n+m!=0) 88 if(((t-(n+m))&1)) 89 { 90 puts("0"); 91 return 0; 92 } 93 ans=L(t,t/2-(abs(n-m)/2))*L(t,(t-n-m)/2)%mod; 94 printf("%lld",ans); 95 return 0; 96 }
T3.光
没想到一道模拟题卡掉了我50分。。。暴力都打错了,骗到20分,考完后发现加clock等一大堆特判可以卡到80分。
正解就是模拟,但是优化了一下。首先,在原来的基础上,可以直接考虑到当前状态的终点:答案显然具有单调性,可以二分答案。
在同一种状态中,横纵坐标之和或差必然不变,因此我们可以将黑块存进保存坐标之和或差的vector中,给每个vector排序后二分答案。
但是我们似乎忽略了一个问题:如何统计答案?
一个非常简单却不好想的结论:若一个块被经过,则光线必然只穿过他的一条对角线。
证明如下:
我们给每两个相邻的方块染上不同的颜色,则同一种状态的光线只经过同种颜色的方块。而除了反向反射的两种状态,另外两种反射方式必然从一种颜色的方块变成另一种,即,经过右下->左上对角线和右上->左下对角线的光线穿过的方块颜色必不相同,所以同一方块不可能被穿过两条对角线。
既然如此,那么就又有一个非常显然的结论:一个方块最多被经过两次,两次穿过同一对角线且方向相反。
那么一个方块被经过两次的条件呢?当且仅当发生了反向的反射。同样,若发生了反向的反射,反向光线会将原光线的所有路径重走一遍,因此,一旦发生反向反射,所有被经历的方块都会被穿过两次。否则,每个方块仅被穿过一次。
因此,我们可以记录是否发生了反向的反射,若发生了,最后统计的答案数/2。计算每种状态的贡献时,只需将起始节点和终点的横(纵)坐标相减即可。
注意反射情况十分复杂,分类讨论即可。为了方便,多调几个STL挺好的。
#include<bits/stdc++.h> #define mk(a,b,c) make_pair(a,make_pair(b,c)) using namespace std; const int zx[4]={-1,-1,1,1},zy[4]={1,-1,1,-1}; int n,m,k; long long ans=0; bool vv; vector<int>h[200005],g[200005]; map<pair<int,pair<int,int> >,bool>c; map<pair<int,int>,bool>kk; int main() { scanf("%d%d%d",&n,&m,&k);int kkk=max(n,m); for(int i=1;i<=k;i++) { int x,y; scanf("%d%d",&x,&y); kk[make_pair(x,y)]=true; h[x+y].push_back(x-y); g[x-y+kkk].push_back(x+y); } for(int i=0,x,y;i<=m+1;i++) { x=0,y=i; kk[make_pair(x,y)]=true; h[x+y].push_back(x-y); g[x-y+kkk].push_back(x+y); x=n+1; kk[make_pair(x,y)]=true; h[x+y].push_back(x-y); g[x-y+kkk].push_back(x+y); } for(int i=0,x,y;i<=n+1;i++) { x=i,y=0; kk[make_pair(x,y)]=true; h[x+y].push_back(x-y); g[x-y+kkk].push_back(x+y); y=m+1; kk[make_pair(x,y)]=true; h[x+y].push_back(x-y); g[x-y+kkk].push_back(x+y); } for(int i=0;i<=kkk+kkk+2;i++) sort(h[i].begin(),h[i].end()), sort(g[i].begin(),g[i].end()); int x,y,zt; string ss; cin>>x>>y>>ss; if(ss=="NE") zt=0; if(ss=="NW") zt=1; if(ss=="SE") zt=2; if(ss=="SW") zt=3; if(zt==0) { int cc=*--upper_bound(h[x+y].begin(),h[x+y].end(),x-y),xx=(x+y+cc)>>1,yy=(x+y-cc)>>1; if((kk.count(make_pair(xx+1,yy))&&kk.count(make_pair(xx,yy-1)))||((!kk.count(make_pair(xx+1,yy)))&&(!kk.count(make_pair(xx,yy-1))))) { zt=3,x=xx+1,y=yy-1; } else if(kk.count(make_pair(xx+1,yy))) { zt=1,x=xx,y=yy-1; } else if(kk.count(make_pair(xx,yy-1))) { zt=2,x=xx+1,y=yy; } } else if(zt==3) { int cc=*upper_bound(h[x+y].begin(),h[x+y].end(),x-y),xx=(x+y+cc)>>1,yy=(x+y-cc)>>1; if((kk.count(make_pair(xx-1,yy))&&kk.count(make_pair(xx,yy+1)))||((!kk.count(make_pair(xx-1,yy)))&&(!kk.count(make_pair(xx,yy+1))))) { zt=0,x=xx-1,y=yy+1; } else if(kk.count(make_pair(xx-1,yy))) { zt=2,x=xx,y=yy+1; } else if(kk.count(make_pair(xx,yy+1))) { zt=1,x=xx-1,y=yy; } } else if(zt==1) { int cc=*--upper_bound(g[x-y+kkk].begin(),g[x-y+kkk].end(),x+y),xx=(x-y+cc)>>1,yy=(cc-x+y)>>1; if((kk.count(make_pair(xx+1,yy))&&kk.count(make_pair(xx,yy+1)))||((!kk.count(make_pair(xx+1,yy)))&&(!kk.count(make_pair(xx,yy+1))))) { zt=2,x=xx+1,y=yy+1; } else if(kk.count(make_pair(xx+1,yy))) { zt=0,x=xx,y=yy+1; } else if(kk.count(make_pair(xx,yy+1))) { zt=3,x=xx+1,y=yy; } } else if(zt==2) { int cc=*upper_bound(g[x-y+kkk].begin(),g[x-y+kkk].end(),x+y),xx=(x-y+cc)>>1,yy=(cc-x+y)>>1; if((kk.count(make_pair(xx-1,yy))&&kk.count(make_pair(xx,yy-1)))||((!kk.count(make_pair(xx-1,yy)))&&(!kk.count(make_pair(xx,yy-1))))) { zt=1,x=xx-1,y=yy-1; } else if(kk.count(make_pair(xx+1,yy))) { zt=3,x=xx,y=yy-1; } else if(kk.count(make_pair(xx,yy+1))) { zt=0,x=xx-1,y=yy; } } while(!c.count(mk(x,y,zt))) { c[mk(x,y,zt)]=true; if(zt==0) { int cc=*--upper_bound(h[x+y].begin(),h[x+y].end(),x-y),xx=(x+y+cc)>>1,yy=(x+y-cc)>>1; ans+=abs(x-xx); if((kk.count(make_pair(xx+1,yy))&&kk.count(make_pair(xx,yy-1)))||((!kk.count(make_pair(xx+1,yy)))&&(!kk.count(make_pair(xx,yy-1))))) { zt=3,x=xx+1,y=yy-1,vv=true; } else if(kk.count(make_pair(xx+1,yy))) { zt=1,x=xx,y=yy-1; } else if(kk.count(make_pair(xx,yy-1))) { zt=2,x=xx+1,y=yy; } } else if(zt==3) { int cc=*upper_bound(h[x+y].begin(),h[x+y].end(),x-y),xx=(x+y+cc)>>1,yy=(x+y-cc)>>1; ans+=abs(x-xx); if((kk.count(make_pair(xx-1,yy))&&kk.count(make_pair(xx,yy+1)))||((!kk.count(make_pair(xx-1,yy)))&&(!kk.count(make_pair(xx,yy+1))))) { zt=0,x=xx-1,y=yy+1,vv=true; } else if(kk.count(make_pair(xx-1,yy))) { zt=2,x=xx,y=yy+1; } else if(kk.count(make_pair(xx,yy+1))) { zt=1,x=xx-1,y=yy; } } else if(zt==1) { int cc=*--upper_bound(g[x-y+kkk].begin(),g[x-y+kkk].end(),x+y),xx=(x-y+cc)>>1,yy=(cc-x+y)>>1; ans+=abs(x-xx); if((kk.count(make_pair(xx+1,yy))&&kk.count(make_pair(xx,yy+1)))||((!kk.count(make_pair(xx+1,yy)))&&(!kk.count(make_pair(xx,yy+1))))) { zt=2,x=xx+1,y=yy+1,vv=true; } else if(kk.count(make_pair(xx+1,yy))) { zt=0,x=xx,y=yy+1; } else if(kk.count(make_pair(xx,yy+1))) { zt=3,x=xx+1,y=yy; } } else if(zt==2) { int cc=*upper_bound(g[x-y+kkk].begin(),g[x-y+kkk].end(),x+y),xx=(x-y+cc)>>1,yy=(cc-x+y)>>1; ans+=abs(x-xx); if((kk.count(make_pair(xx-1,yy))&&kk.count(make_pair(xx,yy-1)))||((!kk.count(make_pair(xx-1,yy)))&&(!kk.count(make_pair(xx,yy-1))))) { zt=1,x=xx-1,y=yy-1,vv=true; } else if(kk.count(make_pair(xx+1,yy))) { zt=3,x=xx,y=yy-1; } else if(kk.count(make_pair(xx,yy+1))) { zt=0,x=xx-1,y=yy; } } } printf("%lld\n",vv?ans/2:ans); return 0; }