码迷,mamicode.com
首页 > 其他好文 > 详细

[考试反思]0410省选模拟67:迷惑

时间:2020-04-12 12:29:11      阅读:64      评论:0      收藏:0      [点我收藏+]

标签:img   生成   i+1   复杂度   printf   字典   怎么   cas   back   

技术图片

技术图片

现在想想,先做$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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

[考试反思]0410省选模拟67:迷惑

标签:img   生成   i+1   复杂度   printf   字典   怎么   cas   back   

原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12677560.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!