[考试反思]1217省选模拟1: 苟且
的确扛过了联赛,然后貌似也做好了心理准备。
但是这。。。
再加上联赛的先天劣势。。。
先不用说翻盘,好歹省选跟别人追平,才有资格谈翻盘吧?
(然而其实就算追平了也没资格,还差十几分)
省选这个难度,追十几分。。。
最近烦心的事是真的多。。。扛过这一段,也许一切都会好起来吧。。。
省选模拟4个小时,并没有想象中那么漫长。
难度预估全错:T2<T1<T3。与现在所认为的难度顺序,只是大于号全部想成了小于号。。。
然后自然,时间分配就全乱了。
看到T3树论?还放在T3的位置上?啥还NPC了?大神题不敢做,然后就丢掉了唯一一个可能想出来的题。
坐在我右边的三个大神都切了我给我这一排丢人了2333
然后就一直在找T1和T2的规律。
刚开始T1还没看到保证P是质数白YY了半天。
后来在想的时候,想到指数肯定不能直接算,所以应该要用原根。然后想了一会脑子里萌生了一个想法:
不是所有的质数都有原根!
然后我就歇比了我也不知道我是怎么想的反正这个“灵光一现”就帮助T1把我切了。
T2显然是找不出来规律的,但是我花费了大量的时间。。。然后写的搜索懒得打剪枝就丢了10分。
懒得打。。。?其实我算了一下复杂度,差不多是对了就扔了,,然而并没有发现无良玩意有1000组测试数据。。。
过了一会离考试结束还有20分钟发现T1的20分暴力貌似会TLE0,急忙修改,改的时候灵感突现顺手写了个40分的不会证算法。
发现这个很适合打表,但是还有十几分钟。。?
最后就拿到了这个啥也不是的成绩。
希望明天不要这么蠢了。。。关键是要静下心来。。。
题是真的难,而且部分分的确没什么区分度(基本所有人都想的出来而且分值小),所以还是要冲着正解去不然3道题加起来才100分玩个啥啊。。。
T1:天空碎片
题意:求$1<=n,m<=P(P-1)$满足$n^m \equiv m^n (mod \ P)$。数据范围:$P \le 10^{12} $。答案对给定模数取模。100组测试数据。
一看到题目里那个P(P-1)就知道事情一定不简单。对于一个幂,底数可以对p取模,指数可以对(p-1)取模。
所以P(P-1)就会刚好取遍每一个底数/指数值一次,我们只要统计这样的值对P取模后有多少种放进桶里,平方后就是对答案的贡献。
1 #include<cstdio> 2 #define int long long 3 int mod,x,buc[1111]; 4 main(){ 5 int t,ans; 6 scanf("%lld%lld",&t,&mod); 7 while(t--){ans=0; 8 scanf("%lld",&x); 9 for(int i=0,w=1;w=1,i<x;++i)for(int j=0;j<x-1;++j,w=w*i%x)buc[w]++; 10 buc[0]++;buc[1]--; 11 for(int i=0;i<x;++i)ans+=buc[i]*buc[i]%mod,buc[i]=0; 12 printf("%lld\n",ans%mod); 13 } 14 }
然后代入原根,问题转化为问有多少数对(a,b,c,d)满足$ab \equiv cd (mod \ P-1)$,其中$0 \le a,b,c,d < P-1$。在这里没有考虑i和j直接是P的倍数。
含义?就是原根的指数取模后相同,然后就同余了啊。
可以发现,可以CRT掉把问题分解为质数层面,答案相乘,因为根据两个互质数点对一定可以得到一个在它们条件下都满足的点对。
设上面所说的这个定义为函数F(P-1),现在已经分解为$\prod F(p_i^{e_i})$
对于一个质数的幂求解,问题就是看模x之后ab的取值。设$C(i)$为$ab \equiv i$的ab对数。
然后既然是对质数考虑,那么我们就把abi都对p分解。枚举它们分别含有p的几次幂
太累了粘题解了。其中C(0)那一项看着简单实际要化大量的式子。要考虑a'或b'为0的情况再加>=e的情况,再拼凑式子。
就是分别枚举alpha和beta,保证它们的和大于e,再动用等比数列求和拆和式,最后就是一波因式分解+合并同类项。
然后根据含义$ F(p_i^{e_i})=\sum\limits_{i=0}^{p_i^{e_i}-1} C^2(i)$
然后得到答案$ans=(P-1)^2 + F(P-1)$。在这里又一次考虑了整除的ab。
挺恶心的。注意取模。
1 #include<cstdio> 2 #define int long long 3 int t,mod,P,pr[22],E[22],pc,phi[22]; 4 int pow(int b,int t,int a=1){for(;t;t>>=1,b=b*b%mod)if(t&1)a=a*b%mod;return a;} 5 main(){ 6 scanf("%lld%lld",&t,&mod); 7 while(t--){ 8 scanf("%lld",&P);P--;int rp=P%mod;pc=0; 9 for(int i=2;i*i<=P;++i)if(P%i==0){ 10 pr[++pc]=i;E[pc]=0;phi[pc]=1; 11 while(P%i==0)P/=i,E[pc]++,phi[pc]*=i; 12 phi[pc]=phi[pc]/i*(i-1)%mod; 13 }if(P!=1)pr[++pc]=P,E[pc]=1,phi[pc]=(P-1)%mod; 14 int ans=1; 15 for(int i=1,p,e;p=pr[i],e=E[i],i<=pc;++i){ 16 int w=pow(phi[i]*(e+1)%mod+pow(p,e-1),2); 17 for(int j=0;j<e;++j)w=(w+pow(p,e-j-1)*(p-1)%mod*pow((j+1)*phi[i]%mod,2))%mod; 18 ans=ans*w%mod; 19 } 20 printf("%lld\n",(ans+rp*rp)%mod); 21 } 22 }
T2:未来拼图
题意:长度为N的对称整数数列循环卷积得到数列F,给定F求原数列个数及字典序最小的方案。$N \le 25 ,T \le 1000$
需要比较深刻的理解DFT,而不是直接跳过它得到FFT。
可以认为,DFT处理的本质问题就是循环卷积(详见Cage的证明),而FFT把长度开成了两倍来防止超出部分循环回去。
所以只需要做1倍长度的DFT得到点值,点值开根得到插值,判断方案是否合法即可。
当然长度才25,暴力$O(n^2)$的插值求值就可以了,写FFT常数可能反而更大。。。
而在开根时每个复数(除0外)都有2个根,要枚举到所有情况。而因为数列是对称的,所以只需要确定其中一侧的复数取值就可以确定另一侧。
可能会卡常。把所有单位复数根都预处理出来就飞快了。
总复杂度$O(T 2^{\frac{N}{2} +1} N^2)$。
1 #include<bits/stdc++.h> 2 using namespace std; 3 map<vector<int>,int>M; 4 vector<int>v; 5 const double eps=1e-5,pi=3.141592653589793238; 6 struct cp{ 7 double r,i; 8 cp(double re=0,double im=0){r=re;i=im;} 9 cp operator+(cp b){return cp(r+b.r,i+b.i);} 10 cp operator-(cp b){return cp(r-b.r,i-b.i);} 11 cp operator*(cp b){return cp(r*b.r-i*b.i,r*b.i+i*b.r);} 12 void operator=(int x){r=x;i=0;} 13 cp sqrt(){ 14 if(fabs(r)<eps&&fabs(i)<eps)return cp(0,0); 15 double alpha=atan2(i,r)/2,len=std::sqrt(std::sqrt(r*r+i*i)); 16 return cp(cos(alpha)*len,sin(alpha)*len); 17 } 18 }A[27],P[27],B[27],C[27],R[27],Q[27],W[27][27][27]; 19 int t,n,cnt,r[27],c[27]; 20 void DFT(cp*A,cp*B,int n){ 21 for(int i=0;i<n;++i)B[i]=0; 22 for(int i=0;i<n;++i){ 23 cp w=cp(cos(pi*2/n*i),sin(pi*2/n*i)),t=cp(1,0); 24 for(int j=0;j<n;++j)B[i]=B[i]+A[j]*t,t=t*w; 25 } 26 } 27 void IDFT(cp*A,cp*B,int n){ 28 for(int i=0;i<n;++i)B[i]=0; 29 for(int i=0;i<n;++i)for(int j=0;j<n;++j)B[i]=B[i]+A[j]*W[n][i][j]; 30 for(int i=0;i<n;++i)B[i].r/=n,B[i].i/=n; 31 } 32 void update(){ 33 for(int i=0;i<n;++i)if(c[i]>r[i])break;else if(c[i]<r[i])return; 34 for(int i=0;i<n;++i)c[i]=r[i]; 35 } 36 main(){ 37 scanf("%d",&t); 38 for(int n=0;n<=25;++n)for(int i=0;i<n;++i)for(int j=0;j<n;++j)W[n][i][j]=cp(cos(-pi*2/n*i*j),sin(-pi*2/n*i*j)); 39 while(t--){ 40 scanf("%d",&n);cnt=0;c[0]=12346567; 41 for(int i=0,x;i<n;++i)scanf("%d",&x),P[i]=x; 42 DFT(P,A,n); 43 for(int i=0;i<n;++i)B[i]=A[i].sqrt(),C[i]=cp(-B[i].r,-B[i].i); 44 for(int i=0;i<1<<n/2+1;++i){ 45 for(int j=0;j<n/2+1;++j)if(i&1<<j)R[j]=B[j],R[n-j]=B[n-j];else R[j]=C[j],R[n-j]=C[n-j]; 46 IDFT(R,Q,n); 47 for(int j=0;j<n;++j)if(floor(Q[j].r-eps)==floor(Q[j].r+eps))goto fail;else r[j]=floor(Q[j].r+eps); 48 for(int j=1;j<n;++j)if(r[j]!=r[n-j]||r[j]<0)goto fail; 49 if(r[0]<0)goto fail; 50 v.clear();for(int i=0;i<n;++i)v.push_back(r[i]); 51 if(M.find(v)!=M.end())continue;M[v]; 52 cnt++;update();fail:; 53 } 54 printf("%d ",cnt);if(cnt)for(int i=0;i<n;++i)printf("%d ",c[i]);puts("");M.clear(); 55 } 56 }
T3:完美理论
题意:(假装是个NPC问题)给两棵树,点编号相同权值相同,要求选出一个最大权点集在两棵树上都是联通块。$n\le 100,T \le 50$
所以它并不是NPC,也并不是树论,也并不是dp。
当我们考虑一个点集时,发现它有一定依赖关系。
两棵树都是无根的,我们钦定一个根,如果保证根必选,那么就必须有「选了儿子就必须选父亲」的限制。
枚举根,跑最大权闭合子图。
我为什么会怂这个题啊。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int fir[102],l[202],to[202],FIR[102],L[202],TO[202],n,t,w[102],ec,EC,ans; 4 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 5 void Link(int a,int b){L[++EC]=FIR[a];FIR[a]=EC;TO[EC]=b;} 6 int wfir[102],wl[888],wto[888],ww[888],Ec,q[111],d[111]; 7 void wlink(int a,int b,int W){wl[++Ec]=wfir[a];wfir[a]=Ec;wto[Ec]=b;ww[Ec]=W;} 8 void con(int a,int b,int W){wlink(a,b,W);wlink(b,a,0);} 9 void dfs(int p,int fa){for(int i=fir[p];i;i=l[i])if(to[i]!=fa)con(to[i],p,10000000),dfs(to[i],p);} 10 void DFS(int p,int fa){for(int i=FIR[p];i;i=L[i])if(TO[i]!=fa)con(TO[i],p,10000000),DFS(TO[i],p);} 11 bool bfs(){ 12 for(int i=1;i<=n+1;++i)d[i]=0;d[0]=1; 13 for(int h=1,t=1;h<=t;++h)for(int i=wfir[q[h]];i;i=wl[i])if(!d[wto[i]]&&ww[i]) 14 d[q[++t]=wto[i]]=d[q[h]]+1; 15 return d[n+1]; 16 } 17 int dinic(int p,int flow){int r=flow; 18 if(p==n+1)return r; 19 for(int i=wfir[p];i&&r;i=wl[i])if(ww[i]&&d[wto[i]]==d[p]+1){ 20 int x=dinic(wto[i],min(r,ww[i])); 21 if(!x)d[wto[i]]=0; 22 ww[i]-=x;ww[i^1]+=x;r-=x; 23 }return flow-r; 24 } 25 int main(){ 26 scanf("%d",&t); 27 while(t--){ 28 scanf("%d",&n);ans=0; 29 for(int i=1;i<=n;++i)fir[i]=FIR[i]=0;ec=EC=0; 30 for(int i=1;i<=n;++i)scanf("%d",&w[i]); 31 for(int i=1,x,y;i<n;++i)scanf("%d%d",&x,&y),link(x,y),link(y,x); 32 for(int i=1,x,y;i<n;++i)scanf("%d%d",&x,&y),Link(x,y),Link(y,x); 33 for(int i=1;i<=n;++i){ 34 for(int j=0;j<=n+1;++j)wfir[j]=0;Ec=1;int mx=0; 35 for(int j=1;j<=n;++j)if(w[j]>0)con(0,j,w[j]),mx+=w[j];else con(j,n+1,-w[j]); 36 dfs(i,0);DFS(i,0);while(bfs())mx-=dinic(0,10000000); 37 ans=max(ans,mx); 38 } 39 printf("%d\n",ans); 40 } 41 }