[考试反思]0522省选模拟103:败阵
这次不把自己排名粘过来,因为排名是假的。
$T2$写的是暴力,$20pts$,自己造了组数据就给自己卡掉了。
卡完之后剩总分$30$,倒数第二,那就没什么好说的了。
最近状态好差啊。
其实$T3$是想到正解了,最后暴写,没有写完。
$T2$正解思路已经有了也没有写觉得暴力可以卡过就停了。
真不知道自己这四个半小时都干了点啥。快开学吧!!
T1:求和
大意:给定$n,m$求$\sum\limits_{i=0}^{n} \sum\limits_{j=0}^{m} \binom{i}{j} [i \equiv 0 (\mod 2)][j \equiv 0 (\mod 2)]$。$n\le 10^9,m \le 10^6$。模数不定。
$m$的范围可以枚举,我们设$f_i$表示$\sum\limits_{j=0}^{\frac{n}{2}} \binom{2j}{i}$。那么我们要求的就是$\sum\limts_{i=0}^{\frac{m}{2}} f(2i)$
同理设一个$g_i$表示$\sum\limits_{j=0}^{\frac{n}{2}} \binom{2j+1}{i}$。
观察这个东西在杨辉三角上的形状,不难发现$f_i+f_{i+1} =g_{i+1}$
根据组合恒等式我们还有$f_i+g_i=\binom{n+1}{i+1}$
联立一下可以得到$f_i=\frac{\binom{n+1}{i+1} - f_{i-1}}{2}$
显然$f_0=\frac{n+1}{2}$。这样在模数为奇数的时候就可以直接递推了。
上面那个式子还等价于$f_i=\binom{n+1}{i+2} - 2f_{i+1}$。也就是说我们还可以倒着推回来。
所以我们只需要知道$f_m$然后倒推回来就可以。
我们把$f_{i+1}=\binom{n+1}{i+3} - 2f_{i+2}$(和上面等价)带回上面的式子得到$f_i=\binom{n+1}{i+2}-2\binom{n+1}{i+3} +4 f_{i+2}$
同理我们代入$f_{i+2}$。一直代可以得到$f_i=\sum\limits_{j=0}^{\infty} (-2)^j \binom{n+1}{i+2+j}$
因为模数是奇数我们已经算出来了,再算出偶数然后$CRT$就可以。也就是我们要计算这东西对$2^r$取模。
上面这个项数一多之后模$2^r$就是$0$了。所以只要爆算$r$个组合数然后递推就行了。
1 #include<cstdio> 2 #define S 1000089 3 int n,m,k,P,A,pw[666],C[S],Ct[S]; 4 void exgcd(int a,int b,int&x,int&y){ 5 if(!b){x=1;y=0;return;} 6 exgcd(b,a%b,x,y);int r=x;x=y;y=r-a/b*y; 7 } 8 int inv(int a,int m){int x,y;exgcd(a,m,x,y);return (x%m+m)%m;} 9 void solve(int p,int k,int pk){ 10 pw[0]=1;for(int i=1;i<k;++i)pw[i]=pw[i-1]*p; 11 if(p==2){ 12 C[0]=1; 13 for(int i=1;i<S;++i){ 14 Ct[i]=Ct[i-1]; 15 int x=n+2-i; while(x&&x%p==0)x/=p,Ct[i]++; C[i]=1ll*C[i-1]*x%pk; 16 x=i; while(x%p==0)x/=p,Ct[i]--; C[i]=1ll*C[i]*inv(x,pk)%pk; 17 }int F=0; 18 for(int i=0;i<k;++i)F=(F+(i&1?pk-1ll:1ll)*pw[Ct[m+2+i]+i]%pk*C[m+2+i]%pk+pk)%pk; 19 A=m&1?0:F; 20 for(int i=m-1;~i;--i)F=(1ll*C[i+2]*pw[Ct[i+2]]-2*F%pk+pk)%pk,A=(A+(i&1?0:F))%pk; 21 P=pk;return; 22 } 23 int C=n+1,Ct=0,F=n+1>>1,a=F; 24 while(C%p==0)C/=p,Ct++; 25 for(int i=1;i<=m;++i){ 26 int x=n+1-i; while(x&&x%p==0)x/=p,Ct++; C=1ll*C*x%pk; 27 x=i+1; while(x%p==0)x/=p,Ct--; C=1ll*C*inv(x,pk)%pk; 28 F=((Ct>=k?0:1ll*C*pw[Ct])-F+pk)%pk*(pk+1>>1)%pk; 29 if(!(i&1))a=(a+F)%pk; 30 }int _P=P*pk; 31 if(P)A=(A*1ll*inv(pk,P)%_P*pk+a*1ll*inv(P,pk)%_P*P)%_P,P=_P; 32 else P=pk,A=a; 33 } 34 int main(){ 35 scanf("%d%d%d",&n,&m,&k); if(n%2==0)n++; 36 for(int i=2;i*i<=k;++i)if(k%i==0){ 37 int t=0,pk=1; 38 while(k%i==0)k/=i,t++,pk*=i; 39 solve(i,t,pk); 40 }if(k>1)solve(k,1,k); 41 printf("%d",A); 42 }
T2:农民(farmer)
大意:维护二叉树,支持:1,修改点权。2,子树内所有节点$x$,交换$lc_x,rc_x$。3,询问根到某个节点的路径上所有点是否满足二叉搜索树性质。$n,m \le 10^5$
每一条边对应着一种限制,如果是指向左儿子的边就说明限制子树内所有点的权值都要$< x$才可能被到达。否则就是大于。
树剖线段树,边权下放到点权,然后就是区间查询最大/小值,区间打翻转标记,单点修改。额外维护一个反转后的限制就好了。$O(mlogn)$
1 #include<cstdio> 2 int min(int a,int b){return a<b?a:b;} 3 int max(int a,int b){return a>b?a:b;} 4 const int S=100005,inf=2e9; 5 int v[S],n,m,dfn[S],tim,son[S],sz[S],top[S],f[S],idfn[S],ls[S],rs[S],dfr[S]; 6 void dfs(int p,int fa){ 7 sz[p]=1; f[p]=fa; 8 if(ls[p])dfs(ls[p],p),sz[p]+=sz[ls[p]]; 9 if(rs[p])dfs(rs[p],p),sz[p]+=sz[rs[p]]; 10 son[p]=sz[ls[p]]>sz[rs[p]]?ls[p]:rs[p]; 11 } 12 void DFS(int p,int tp){ 13 dfn[p]=++tim; top[p]=tp; idfn[tim]=p; 14 if(son[p])DFS(son[p],tp); 15 if(!dfn[ls[p]])DFS(ls[p],ls[p]); 16 if(!dfn[rs[p]])DFS(rs[p],rs[p]); 17 dfr[p]=tim; 18 } 19 #define lc p<<1 20 #define rc lc|1 21 #define md (L+R>>1) 22 int lz[S<<2],le[2][S<<2],ge[2][S<<2],Le,Ge; 23 void up(int p){ 24 le[0][p]=min(le[lz[lc] ][lc],le[lz[rc] ][rc]); 25 le[1][p]=min(le[lz[lc]^1][lc],le[lz[rc]^1][rc]); 26 ge[0][p]=max(ge[lz[lc] ][lc],ge[lz[rc] ][rc]); 27 ge[1][p]=max(ge[lz[lc]^1][lc],ge[lz[rc]^1][rc]); 28 } 29 void down(int p){if(lz[p])lz[lc]^=1,lz[rc]^=1,lz[p]=0;} 30 void build(int l,int r,int p=1,int L=1,int R=n){ 31 if(L==R){ 32 int x=idfn[L],d=rs[f[x]]==x,V=v[f[x]]; 33 le[0][p]=le[1][p]=inf,ge[0][p]=ge[1][p]=-inf; 34 if(x!=1)ge[d^1][p]=le[d][p]=V; 35 return; 36 }down(p); if(l<=md)build(l,r,lc,L,md); if(r>md)build(l,r,rc,md+1,R); up(p); 37 } 38 void ask(int l,int r,int p=1,int L=1,int R=n){ 39 if(l<=L&&R<=r){Le=min(Le,le[lz[p]][p]);Ge=max(Ge,ge[lz[p]][p]);return;} 40 down(p); if(l<=md)ask(l,r,lc,L,md); if(r>md)ask(l,r,rc,md+1,R); up(p); 41 } 42 void mdf(int l,int r,int p=1,int L=1,int R=n){ 43 if(l<=L&&R<=r){lz[p]^=1;return;} 44 down(p); if(l<=md)mdf(l,r,lc,L,md); if(r>md)mdf(l,r,rc,md+1,R); up(p); 45 } 46 bool query(int p){ 47 Le=inf;Ge=-inf; int x=p; 48 while(p&&Le>v[x]&&v[x]>Ge)ask(dfn[top[p]],dfn[p]),p=f[top[p]]; 49 return Le>v[x]&&v[x]>Ge; 50 } 51 int main(){ 52 scanf("%d%d",&n,&m); dfn[0]=1; 53 for(int i=1,a,b;i<=n;++i)scanf("%d%d%d",&v[i],&ls[i],&rs[i]); 54 dfs(1,0);DFS(1,1);build(1,n); 55 for(int i=1,op,x,y;i<=m;++i){ 56 scanf("%d%d",&op,&x); 57 if(op==1){scanf("%d",&v[x]);if(ls[x])build(dfn[ls[x]],dfn[ls[x]]);if(rs[x])build(dfn[rs[x]],dfn[rs[x]]);} 58 else if(op==2&&dfr[x]>dfn[x])mdf(dfn[x]+1,dfr[x]); 59 else if(op==3)puts(query(x)?"YES":"NO"); 60 } 61 }
T3:仙人掌
大意:给定仙人掌和数列$a$,要求边定向都$deg_\le a_i$。求方案数。$n\le 10^5$
如果问题是在树上那么就是一个简单的背包$dp$。
儿子合并的复杂度不对,可以用分治$FFT$来搞。
设$dp[0/1][p]$表示当前节点$p$的父亲边指向父亲还是指向自己然后就这么转移就行了。$O(nlog^2n)$
仙人掌的话,解决办法通常有两种:圆方树和$dfs$树。
后者看起来会简单一些。但是在这道题里的思路都是断环成链。
维护$dp[0/1][0/1][p]$表示父亲边指向,跨过该点的返祖边的指向,的方案数。
不要想复杂,稍微分类讨论一下就好了。好写不好调。动不动就又只过了仙人球的数据。滚去自闭。还是$O(nlog^2n)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define pb push_back 4 #define y to[i] 5 const int S=888888,mod=998244353; 6 int len=1,rev[S]; 7 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 8 void sat(int x){ 9 len=1;while(len<=x)len<<=1; 10 for(int i=1;i<len;++i)rev[i]=rev[i>>1]>>1|(i&1?len>>1:0); 11 } 12 int mo(int x){return x>=mod?x-mod:x;} 13 void NTT(int*a,int op=1){ 14 for(int i=1;i<len;++i)if(rev[i]>i)swap(a[i],a[rev[i]]); 15 for(int i=1;i<len;i<<=1)for(int j=0,w=qp(3,(mod-1)/2/i*op+mod-1);j<len;j+=i<<1) 16 for(int k=j,t=1,x,z;k<j+i;++k,t=1ll*t*w%mod) 17 x=a[k],z=a[k+i]*1ll*t%mod,a[k]=mo(x+z),a[k+i]=mo(x-z+mod); 18 if(op==-1)for(int i=0,iv=qp(len,mod-2);i<len;++i)a[i]=1ll*a[i]*iv%mod; 19 } 20 int fir[S],l[S],to[S],ec=1,te[S],n,m; 21 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 22 int anc[S],al[S],a[S],dp[2][2][S]; 23 vector<int>F[S],v0,v1,v2; 24 #define lc p<<1 25 #define rc lc|1 26 #define md (l+r>>1) 27 void build(int p,int l,int r){ 28 static int A[S],B[S]; F[p].clear(); 29 if(l==r){F[p].pb(v0[l]);F[p].pb(v1[l]);if(v2.size()&&v2[l])F[p].pb(v2[l]);return;} 30 build(lc,l,md);build(rc,md+1,r); 31 int L;sat(L=F[lc].size()+F[rc].size()-1); 32 for(int i=0;i<len;++i)A[i]=i<F[lc].size()?F[lc][i]:0; 33 for(int i=0;i<len;++i)B[i]=i<F[rc].size()?F[rc][i]:0; 34 NTT(A);NTT(B);for(int i=0;i<len;++i)A[i]=1ll*A[i]*B[i]%mod;NTT(A,-1); 35 for(int i=0;i<L;++i)F[p].pb(A[i]); F[lc].clear();F[rc].clear(); 36 } 37 void dfs(int p,int fe){ 38 al[p]=al[to[fe^1]]+1; int bg=0,son=0; 39 for(int i=fir[p];i;i=l[i]) 40 if(!al[y])dfs(y,i),anc[p]|=anc[y]==p?0:anc[y],son|=anc[y]&&anc[y]!=p?y:0,te[i]=1; 41 else if(al[y]<al[p]&&(i^fe)!=1)anc[p]=y,bg=1; 42 for(int _=0;_<2;++_){ 43 v0.clear();v1.clear();v2.clear(); 44 for(int i=fir[p];i;i=l[i])if(te[i]) 45 if(anc[y]==p)v0.pb(dp[1][0][y]),v1.pb(mo(dp[0][0][y]+dp[1][1][y])),v2.pb(dp[0][1][y]); 46 else v0.pb(dp[_][0][y]),v1.pb(dp[_][1][y]),v2.pb(0); 47 if(v0.size())build(1,0,(int)v0.size()-1);else F[1].clear(),F[1].pb(1); int sz=F[1].size(); 48 for(int i=0;i<sz&&i< a[p]-(bg&_);++i)dp[_][0][p]=mo(dp[_][0][p]+F[1][i]); 49 for(int i=0;i<sz&&i<=a[p]-(bg&_);++i)dp[_][1][p]=mo(dp[_][1][p]+F[1][i]); 50 } 51 } 52 char s[1<<26],*p=s; 53 void In(int&a){a=0;while(!isdigit(*p))p++;while(isdigit(*p))a=a*10+*p++-48;} 54 int main(){fread(s,1,1<<26,stdin); 55 In(n);In(m); 56 for(int i=1,x,z;i<=m;++i)In(x),In(z),link(z,x),link(x,z); 57 for(int i=1;i<=n;++i)In(a[i]); 58 dfs(1,0); printf("%d\n",dp[0][1][1]); 59 }
$\sum\limits_{j=0}^{\frac{n}{2}} \binom{2j}{i}$。