[考试反思]0410省选模拟67:迷惑
现在想想,先做$T3$真乃人间迷惑行为。
部分分不多的一场考试,$T1$部分分最多结果没花时间光荣爆零,结果正解真的就是一个暴力+大力分类讨论
$T2$也比较可想,然而看着$75pts$的子任务心中有几分慌张,苟了个暴力跑路了。
结果一个弱智$T3$的$10pts$部分分$O(n^8)$暴写了$2.8k$。从这代码长度看是不是$10pts$您施舍的有点少啊。
其余部分分一点用没有,不知道咋想的。
T1:链
大意:维护操作:加边,询问有几个点满足:删掉之后图中剩下的都是若干链。$n,m \le 200000,$
那就分类讨论就完了呗。
链就是,所有点度数$\le 2$,且没有简单环。
如果不出现度数$>2$的点:
若有超过一个环,答案$0$
若有恰好一个环,答案是环大小
如果没有环,答案是点数
如果出现了度数$> 2$的点,那么当度数$=3$的点第一次出现时,答案只可能是这个点以及与其相连的三个点。
对这四个点分别维护删掉其中一个点之后的四个图,如果依旧有度数$\ge 3$的点或者是出环了这个点就不可行。
维护$5$个度数数组,$5$个并查集,其中一个要维护大小。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 200005 4 vector<int>v[S]; 5 int ok[5],n,m,ans,T,f[5][S],sz[S],deg3,q[5],deg[5][S],cir; char s[9]; 6 int find(int k,int p){return f[k][p]==p?p:f[k][p]=find(k,f[k][p]);} 7 int main(){ 8 cin>>n>>m; ans=n; 9 for(int i=1;i<=n;++i)sz[i]=1; 10 for(int j=0;j<5;++j)for(int i=1;i<=n;++i)f[j][i]=i; 11 for(int i=1,a,b;i<=m;++i){ 12 scanf("%s",s); 13 if(s[0]=='Q')printf("%d\n",ans); 14 else{ 15 scanf("%d%d",&a,&b); 16 if(!ans)continue; 17 v[a].push_back(b);v[b].push_back(a); 18 deg[0][a]++;deg[0][b]++; 19 if(deg[0][a]<deg[0][b])swap(a,b); 20 if(deg3){ 21 for(int z=1;z<=4;++z)if(ok[z]){ 22 if(a==q[z]||b==q[z])continue; 23 ans--; 24 deg[z][a]++; deg[z][b]++; 25 if(deg[z][a]>2||deg[z][b]>2)ok[z]=0; 26 int x=find(z,a),u=find(z,b); 27 if(x==u)ok[z]=0; 28 else f[z][x]=u; 29 ans+=ok[z]; 30 } 31 }else if(deg[0][a]==3){ 32 q[1]=a;q[2]=v[a][0];q[3]=v[a][1];q[4]=v[a][2]; deg3=1; ans=0; 33 for(int z=1;z<=4;++z){ 34 ok[z]=1; 35 for(int i=1;i<=n;++i)if(i!=q[z])for(int j=0;j<v[i].size();++j)if(i<v[i][j]&&v[i][j]!=q[z]){ 36 deg[z][i]++; deg[z][v[i][j]]++; 37 if(deg[z][i]>2)ok[z]=0; 38 if(deg[z][v[i][j]]>2)ok[z]=0; 39 int u=find(z,i),x=find(z,v[i][j]); 40 if(u==x)ok[z]=0; 41 else f[z][u]=x; 42 }ans+=ok[z]; 43 } 44 }else{ 45 a=find(0,a);b=find(0,b); 46 if(a==b&&!cir)cir=1,ans=sz[a]; 47 else if(a==b)ans=0; 48 else sz[a]+=sz[b],f[0][b]=a; 49 } 50 } 51 } 52 }
T2:子集和
大意:给出背包数组(最多$10000$个位置有值),还原字典序最小的物品序列。$|S| \le 60,T \le 100,w_i \le 10^{10}$
首先对于$a_i \ge 0$的点,直接取出当前能被表示出来的最小数,做逆背包就行。
然后对于负数,首先,我们可以发现,整个数列的最大值减去次大值得到的,就是当前剩下的所有数中,绝对值最小者的绝对值。
于是做一遍逆背包把它搞掉。所以我们能用同样的方法得到所有数的绝对值,问题在于定号。
粘一段我给别人讲过的:
考虑这么来理解,这题是一个背包的过程。
01背包的实质,加入一个体积为v的物品时(先考虑正的),类似于将现有的dp数组向右平移v位之后,加到原数组上。
那么考虑v是负的的时候会发生什么,其实只是变成了向左平移。
对于最开始00001020000这个数组,加入一个体积为3的物品,就是先平移成00000001020再叠加得到00001021020.
如果加入一个体积为-3的物品那就是先平移成01020000000再叠加得到01021020000。
我们发现两次加入物品得到的dp数组的值是完全一样的,只不过下标完全错开了v位而已。
所以说,不论取正负,得到的最后的dp数组都是一个样子的,只不过下标不同。
所以当我们确定最大值之后,整个数组就定位了,那么整个数组就一定与题目读入的相符.
为了让字典序最小,我们优先让绝对值大的数负过去。于是我们逐位确定。
做一遍背包,对于尚未确定的值就把正的负的都丢进去,最后看看最大值是否能被表示出来即可。时间复杂度$O(10000T|S|)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 map<long long,int>num,M; 4 long long s[11111],cnt[11111],mx,ans[11111];bool dp[11111]; 5 int main(){ 6 for(int i=0;i<61;++i)num[1ll<<i]=i; 7 int t;cin>>t;for(int T=1;T<=t;++T){ printf("Case #%d: ",T); 8 int _;long long tot=0; cin>>_; 9 for(int i=1;i<=_;++i)scanf("%lld",&s[i]),M[s[i]]=i; mx=s[_]; 10 for(int i=1;i<=_;++i)scanf("%lld",&cnt[i]),tot+=cnt[i]; 11 int n=num[tot],pt2=1,pt1=1; 12 for(int r=1;r<=n;++r){ 13 if(cnt[1]>=2){ 14 ans[r]=0; 15 for(int i=1;i<=_;++i)cnt[i]/=2; 16 }else{ 17 while(!cnt[pt1])pt1++; pt2=pt1+1; 18 while(!cnt[pt2])pt2++; 19 int x=s[pt2]-s[pt1]; ans[r]=x; 20 for(int j=1;j<=_;++j)cnt[M[s[j]+x]]-=cnt[j]; 21 } 22 } 23 sort(ans+1,ans+1+n); 24 for(int i=n;i;--i)if(ans[i]){ 25 ans[i]=-ans[i]; 26 for(int j=1;j<=_;++j)dp[j]=0; 27 dp[M[0]]=1; 28 for(int j=i;j<=n;++j)if(ans[j]>0){for(int k=_;k;--k)if(dp[k])dp[M[s[k]+ans[j]]]|=dp[k];} 29 else for(int k=1;k<=_;++k)if(dp[k])dp[M[s[k]+ans[j]]]|=dp[k]; 30 for(int j=i-1;ans[j];--j){ 31 for(int k=_;k;--k)if(dp[k])dp[M[s[k]+ans[j]]]|=dp[k]; 32 for(int k=1;k<=_;++k)if(dp[k])dp[M[s[k]-ans[j]]]|=dp[k]; 33 }if(!dp[_])ans[i]=-ans[i]; 34 }sort(ans+1,ans+1+n); 35 for(int i=1;i<=n;++i)printf("%lld ",ans[i]); 36 M.clear();puts(""); 37 } 38 }
T3:写作
大意:你有若干互不相同的名词,及物动词,不及物动词,形容词。支持以下句法的情况下你能写多少种文章$F$。$n,adj,vt,vi \le 200$
题解怎么又锅了?
$N=S,N=adj+N,N=n+n+...+n,S=vi,S=N+vt,S=N+vi,S=N+vt+N,P=S,S'=S(S \neq vi),P=S'+S'+...+S',s=P+P+...+P,F=s+s+...+s$
首先你可以简单的记忆化搜索一下。暴写好久就可以得到$10pts$的好成绩。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 1000000007 4 //N= (n+n+...+n) or (s) or (adj+N) 5 //s= (N+vt+N) or (vt+N) or (N+vi) or (vi) 6 //A=(s' +s' +...+s') B=(A+A+...+A) F=(B+B+...+B) 7 //How many kinds of C using all of vt,vi,n,adj exactly once? 8 struct hash_map{ 9 #define R 3860187 10 int fir[R],l[R],to[R],v[R],ec; 11 int&operator[](int x){int r=x%R; 12 for(int i=fir[r];i;i=l[i])if(to[i]==x)return v[i]; 13 l[++ec]=fir[r];fir[r]=ec;to[ec]=x;return v[ec]=-1; 14 } 15 }_F,_A,_B,_S,_N; 16 int&ggt(hash_map&x,int a,int b,int c,int d){return x[a<<24|b<<16|c<<8|d];} 17 int C[222][222],fac[222]; 18 int mo(int a){return a>=mod?a-mod:a;} 19 int S(int vt,int vi,int n,int adj); 20 int N(int vt,int vi,int n,int adj){ 21 int&ans=ggt(_N,vt,vi,n,adj); 22 if(ans!=-1)return ans; 23 ans=(S(vt,vi,n,adj)+(adj?1ll*adj*N(vt,vi,n,adj-1):0))%mod; 24 if(!vt&&!vi&&!adj)ans=mo(ans+fac[n]); 25 return ans; 26 } 27 int S(int vt,int vi,int n,int adj){ 28 int&ans=ggt(_S,vt,vi,n,adj); 29 if(ans!=-1)return ans; 30 if(vi==1&&vt==0&&n==0&&adj==0)return ans=1;ans=0; 31 if(vt)for(int VT=0;VT<vt;++VT)for(int VI=0;VI<=vi;++VI)for(int __N=0;__N<=n;++__N)for(int ADJ=0;ADJ<=adj;++ADJ) 32 ans=(ans+1ll*N(VT,VI,__N,ADJ)*N(vt-VT-1,vi-VI,n-__N,adj-ADJ)%mod*C[vt-1][VT]%mod*C[vi][VI]%mod*C[n][__N]%mod*C[adj][ADJ]%mod*vt)%mod; 33 //cerr<<vt<<' '<<vi<<' '<<n<<' '<<adj<<' '<<ans<<endl; 34 if(vt)ans=(ans+N(vt-1,vi,n,adj)*1ll*vt)%mod; 35 if(vi)ans=(ans+N(vt,vi-1,n,adj)*1ll*vi)%mod; 36 return ans; 37 } 38 int A(int vt,int vi,int n,int adj){ 39 if(!vt&&!vi&&!n&&!adj)return 1; 40 int&ans=ggt(_A,vt,vi,n,adj); 41 if(ans!=-1)return ans;ans=0; 42 for(int VT=0;VT<=vt;++VT)for(int VI=0;VI<=vi;++VI)for(int N=0;N<=n;++N)for(int ADJ=0;ADJ<=adj;++ADJ)if(VT+VI+N+ADJ) 43 ans=(ans+1ll*(S(VT,VI,N,ADJ)-(!VT&&!N&&!ADJ&&VI==1))*A(vt-VT,vi-VI,n-N,adj-ADJ)%mod*C[vt][VT]%mod*C[vi][VI]%mod*C[n][N]%mod*C[adj][ADJ])%mod; 44 return ans; 45 } 46 int B(int vt,int vi,int n,int adj){ 47 if(!vt&&!vi&&!n&&!adj)return 1; 48 int&ans=ggt(_B,vt,vi,n,adj); 49 if(ans!=-1)return ans;ans=0; 50 for(int VT=0;VT<=vt;++VT)for(int VI=0;VI<=vi;++VI)for(int N=0;N<=n;++N)for(int ADJ=0;ADJ<=adj;++ADJ)if(VT+VI+N+ADJ) 51 ans=(ans+1ll*A(VT,VI,N,ADJ)*B(vt-VT,vi-VI,n-N,adj-ADJ)%mod*C[vt][VT]%mod*C[vi][VI]%mod*C[n][N]%mod*C[adj][ADJ])%mod; 52 return ans; 53 } 54 int F(int vt,int vi,int n,int adj){ 55 if(!vt&&!vi&&!n&&!adj)return 1; 56 int&ans=ggt(_F,vt,vi,n,adj); 57 if(ans!=-1)return ans;ans=0; 58 for(int VT=0;VT<=vt;++VT)for(int VI=0;VI<=vi;++VI)for(int N=0;N<=n;++N)for(int ADJ=0;ADJ<=adj;++ADJ)if(VT+VI+N+ADJ) 59 ans=(ans+1ll*B(VT,VI,N,ADJ)*F(vt-VT,vi-VI,n-N,adj-ADJ)%mod*C[vt][VT]%mod*C[vi][VI]%mod*C[n][N]%mod*C[adj][ADJ])%mod; 60 return ans; 61 } 62 int main(){ 63 int vt,vi,n,adj;cin>>vt>>vi>>n>>adj; 64 for(int i=0;i<=200;++i)for(int j=C[i][0]=1;j<=i;++j)C[i][j]=mo(C[i-1][j-1]+C[i-1][j]); 65 for(int i=fac[0]=1;i<=200;++i)fac[i]=fac[i-1]*1ll*i%mod; fac[0]=0; 66 cout<<F(vt,vi,n,adj)<<endl; 67 }
抽象一下问题,我们把一个名词短语$N$(包括i句子$S$)当成树的一个节点。
先不考虑形容词,以及段落$P$与章节$s$,暂且只考虑那种一个文章只有一节一段若干句子的情况。
那么,$N+vt+N$所形成的就是一个有两个儿子的节点。$vt+N$和$N+vi$都是有一个儿子的。$vi$或$n+n+...+n$都可以作为叶节点。
为了简便,称二号点代表有$2$个儿子的点。其余同理。
设一个$A[i]$表示用$i$个带标号二号点构成一棵叶节点空缺的树的方案数,$A[i]=\binom{2i}{i+1} (i-1)!$
设一个$dp[i][j]$表示在有且仅有叶子空缺的情况下,用$i$个带标号的二号点构成$j$棵树的方案数(树与树之间也有序)。直接用$A$转移即可。
这里题解的式子挂掉了,要注意因为是有标号的,所以转移的时候要带一个组合数。
枚举最后一棵树的大小。这里复杂度是$O(n^3)$的。(同级不纠结)
然后考虑一号点,它有零或一个父亲有一个儿子,所以它的作用就是插在任意一条边的中间。
设$g[i][j][k]$表示$i$个二号点$j$个一号点$k$个树的方案数,那么分类讨论这个一号点是新单独生成了一棵树,还是插在了原来的边上(包括树根的上方)
$g[i][j][k]=g[i][j-1][k-1] \times k + g[i][j-1][k] \times (2i+j-1+k)$。题解又锅了,前面因为要考虑树的顺序所以应该乘$k$
这里复杂度也是$O(n^3)$的
最后只需要枚举二号点,一号点,树的数量,即可推得零号点的数量,叶子节点的数量,进而可以知道$n+n+...+n$的名词短语数量。
含义上这需要一个斯特林数,但是不是很对,因为词语之前前后顺序也有关,所以其实是全排列再插板,阶乘乘组合数。
然后再把所有叶子节点做一个选排列(斯特林集合之间有序)。把$vt,vi$分配给各号点是两个组合数。
再考虑形容词的贡献,它是用来修饰名词短语的,所以其实和一号点是类似的,插在边之间就好了,只不过不能插在根的上方。也是个上升幂。
还要考虑的就是段,章节之类的问题。我们发现两个句子之间的间隙只可能是:句尾/段尾/节尾。而且没有限制,所以撑一个$3$的幂就行了。
有一点细节。要注意每个数组的上界是多少。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 1000000007 4 int mo(int a){return a>=mod?a-mod:a;} 5 int C[666][666],A[666],f[233][233],g[201][401][401],S[233][666],fac[401],ans,pw[401],ex[601]; 6 int main(){ 7 f[0][0]=S[0][0]=1; 8 for(int i=fac[0]=pw[0]=1;i<=400;++i)fac[i]=fac[i-1]*1ll*i%mod,pw[i]=pw[i-1]*3ll%mod; 9 for(int i=0;i<=600;++i)for(int j=C[i][0]=1;j<=i;++j)C[i][j]=mo(C[i-1][j-1]+C[i-1][j]); 10 for(int i=0;i<=200;++i)for(int j=1;j<=i;++j)S[i][j]=fac[i]*1ll*C[i-1][j-1]%mod; 11 for(int i=0;i<=600;++i)A[i]=ex[i]=1; 12 for(int i=1;i<=200;++i)for(int j=i<<1;j>i+1;--j)A[i]=A[i]*1ll*j%mod; 13 for(int i=1;i<=200;++i)for(int j=i;j<=200;++j)for(int k=1;k<=j;++k)f[i][j]=(f[i][j]+1ll*f[i-1][j-k]*A[k]%mod*C[j][k])%mod; 14 for(int i=0;i<=200;++i){ 15 for(int j=0;j<=i;++j)g[i][0][j]=f[j][i]; 16 for(int j=1;j+i<=400;++j)for(int k=1;k<=i+j;++k)g[i][j][k]=(g[i][j-1][k-1]*1ll*k+g[i][j-1][k]*(2ll*i+j+k-1))%mod; 17 } 18 int vt,vi,n,adj; cin>>vt>>vi>>n>>adj; 19 for(int i=0;i<=600;++i)for(int j=i+adj-1;j>=i;--j)ex[i]=ex[i]*1ll*j%mod; 20 for(int c2=0;c2<=vt;++c2)for(int c1=vt-c2;c1+c2<=vt+vi;++c1)for(int ts=1;ts<=vt+vi;++ts){ 21 int c0=vt+vi-c2-c1,lf=ts+c2; 22 ans=(ans+1ll*C[vt][c2]*C[vi][c0]%mod*S[n][lf-c0]%mod*g[c2][c1][ts]%mod*C[lf][c0]%mod*fac[c0]%mod*pw[ts-1]%mod*ex[c1+c2*2])%mod; 23 }cout<<ans<<endl; 24 }