[考试反思]0502省选模拟86:恐惧

还是状态持续不佳。但是今天没那么困,虽说脑子还是不想动。

一看到原题就怂,毛病。

好像考场上遇到原题的话,得分会比非原题还低。。。

$T2$作为原题就直接放弃正解了(?),然后乖乖的打部分分,结果教练把子任务放错了于是丢了$15pts$

(放错子任务同时也导致:直接输出$0$能多$20$分。。。啊人就应该有信仰啊为什么我没输出$0$啊。。。)

$T1$的话有一个比较套路的$O(n^3)dp$。

然后一个组合恒等式一小时硬是没看出来耽误很长时间,后来突然觉得自己是弱智就发现了,优化一下过去了。

$T2$的话基本没花时间写暴力跑路。然而虽说$+15pts$貌似影响不大。

$T3$的话是最后没多少时间的时候看的,想写$n^2$暴力的时候顺手加了点剪枝。

写完之后发现:诶这复杂度好像挺对的是$O(n^{1.5})$的然后就快乐的交上去了。

结果被卡常了挂了$30$这又告诉我们一个道理$unordered\ map$不用的元素一定要及时清空。

不然自家$OJ$的速度$O2,4s$跑$2 \times 10^7$都能被卡常。。。

 

T1:人生

大意:求有多少$n$点有向图(随意加边并给所有点黑白染色)满足:边由小编号指向大编号,边端点颜色不同,图中的本质不同路径数为奇数。有些点的颜色已确定。$n \le 2 \times 10^5$

$dp$的味道。

因为所有边都是从小标号指向大标号所以我们就可以按照标号从小到大考虑。

考虑与当前状态相关的元素,只有:前面有几个黑色的$j$满足到$j$的路径数是奇数,白色同理。

$dp[i][j][k]$表示考虑了前$i$个点,有$j$个白色奇数点,有$k$个黑色奇数点。

最暴力的思路是,如果当前点是黑色的就枚举它要与多少个白点连边(剩下的边连不连没影响直接$2$的幂)。可以做到$O(n^4)$

然后发现转移系数只与$j,k$而与当前考虑$i$无关所以可以预处理,是组合数的和。可以做到$O(n^3)$

发现转移系数是$\sum\limits_{i=0}^{\frac{n}{2}} \binom{n}{2i}= \sum\limits_{i=0}^{\frac{n}{2}} \binom{n}{2i+1} = 2^{n-1}$

特殊处理$n=0$。然后转移系数就变成了形如$2^{i-j-1}\times 2^{j-1}=2^{i-2}$就与$j,k$无关了。

只要维护它们是奇数还是偶数,是不是$0$即可。$O(n)$

 1 #include<cstdio>
 2 #define mod 998244353
 3 #define S 200005
 4 int dp[2][3][3],n,c[S],pw[S],ans;
 5 int t(int x){return x==2?1:x^1;}
 6 int main(){
 7     scanf("%d",&n); for(int i=pw[0]=1;i<=n;++i)scanf("%d",&c[i]),pw[i]=pw[i-1]*2%mod;
 8     int nw=0,nx=1; dp[0][2][2]=1;
 9     for(int i=1;i<=n;++i,nw^=1,nx^=1)for(int a=0;a<3;++a)for(int b=0;b<3;++b)if(dp[nw][a][b])
10         if(c[i]==-1)
11             dp[nx][a][b]=(dp[nx][a][b]+1ll*((a!=2?pw[i-2]:0)+(b!=2?pw[i-2]:0))*dp[nw][a][b])%mod,
12             dp[nx][t(a)][b]=(dp[nx][t(a)][b]+1ll*pw[i-1-(b!=2)]%mod*dp[nw][a][b])%mod,
13             dp[nx][a][t(b)]=(dp[nx][a][t(b)]+1ll*pw[i-1-(a!=2)]%mod*dp[nw][a][b])%mod,
14             dp[nw][a][b]=0;
15         else if(c[i])
16             dp[nx][a][b]=(dp[nx][a][b]+1ll*(a!=2?pw[i-2]:0)*dp[nw][a][b])%mod,
17             dp[nx][a][t(b)]=(dp[nx][a][t(b)]+1ll*pw[i-1-(a!=2)]%mod*dp[nw][a][b])%mod,
18             dp[nw][a][b]=0;
19         else 
20             dp[nx][a][b]=(dp[nx][a][b]+1ll*(b!=2?pw[i-2]:0)*dp[nw][a][b])%mod,
21             dp[nx][t(a)][b]=(dp[nx][t(a)][b]+1ll*pw[i-1-(b!=2)]%mod*dp[nw][a][b])%mod,
22             dp[nw][a][b]=0;
23     for(int a=0;a<3;++a)for(int b=0;b<3;++b)if(a+b&1)ans=(ans+dp[nw][a][b])%mod;
24     printf("%d\n",ans);
25 }
View Code

 

T2:赢家(winner)

大意:无向图,求有多少边定向方式使得$1,2$号点可以到达的点存在交集。$n \le 15$

首先直接做不好做,用总方案数减去不存在交集的方案数。

那么我们就可以$O(3^n)$枚举$1,2$号点的可达集合$S,T$。

如果我们求出了将$S$集合内边定向后$1$号点能到达$S$内的所有点的方案数$f(S),g(T)$同理。

那么考虑这个$S,T$对答案的贡献就是:剩余部分的导出子图都随意连边,而剩余部分的点与$S,T$之间的连边都指向$S,T$。当然$S,T$之间不能有边。

问题在于求$f,g$。同理我们用所有方案减去不合法的,枚举真子集表示真实能到达的点集减掉:真实部分是$f(s)$,然后$s$与$S-s$之间的边方向也是确定的,$S-s$内部随便连。

总复杂度$O(n^3)$

 1 #include<cstdio>
 2 #define mod 1000000007
 3 int e[1<<15],n,m,f[1<<15],g[1<<15],ans;
 4 int main(){
 5     scanf("%d%d%*d",&n,&m);
 6     const int S=1<<n;
 7     for(int i=0;i<S;++i)e[i]=1;
 8     for(int i=1,a,b;i<=m;++i){
 9         scanf("%d%d",&a,&b);
10         for(int j=1;j<S;++j)if(j&1<<a-1&&j&1<<b-1)e[j]=e[j]*2%mod;
11     }
12     for(int i=1;i<S;++i)if(i&1){
13         f[i]=e[i];
14         for(int j=i-1&i;j;j=j-1&i)f[i]=(f[i]-1ll*f[j]*e[i^j]%mod+mod)%mod;
15     }
16     for(int i=1;i<S;++i)if(i&2){
17         g[i]=e[i];
18         for(int j=i-1&i;j;j=j-1&i)g[i]=(g[i]-1ll*g[j]*e[i^j]%mod+mod)%mod;
19     }
20     ans=e[S-1];
21     for(int i=1;i<S;++i)for(int u=S-1^i,j=u;j;j=j-1&u)if(e[i|j]==1ll*e[i]*e[j]%mod)ans=(ans-1ll*f[i]*g[j]%mod*e[S-1^i^j])%mod;
22     printf("%d",(ans+mod)%mod);
23 }
View Code

 

T3:黑红兔

大意:给定字符串$s$,要求最大化$k$使得存在$k$个不相交的子串按照左端点排序后,后一个是前一个的严格子串。$n \le 5 \times 10^5$

首先考虑一种暴力:不难发现因为是严格子串所以最优解一定是长度分别为$k,k-1,k-2...3,2,1$的串。

这样答案的级别是$O(\sqrt{n})$的。哈希一下逐层枚举就可以做到$O(n^{1.5})$

设$f[i]$表示最大的长度满足存在一种选法包含$[i,i+f[i]-1]$这个子串

观察$f$的性质,发现左端点左移$1$时$f$最多增加$1$

所以倒着扫一遍,检查当前的$f[i]$是否合法,如果不合法就把以$i+f[i]$开头的所有合法子串都加入,然后使$f[i]--$

考虑一个小优化:如果你在加入所有子串的时候发现当前串已经出现了那么更短的串也一定都加入过了直接跳出。

这样的话复杂度就是$O(n+s)$。$s$表示有多少种本质不同的合法子串(位置不同字符相同当作本质相同)

复杂度大约是线性的。跑得飞快。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ull unsigned long long
 4 unordered_set<ull>M;
 5 int n,f[500005],ans;char s[500005];ull h[500005],pw[500005];
 6 ull hsh(int l,int r){l--;return h[r]-h[l]*pw[r-l];}
 7 void upd(int x){while(f[x]&&!M.count(hsh(x,x+f[x]-1)))M.insert(hsh(x,x+f[x]-1)),f[x]--;}
 8 int main(){
 9     scanf("%s",s+1); while(s[n+1])n++; M.insert(0);
10     for(int i=pw[0]=1;i<=n;++i)pw[i]=pw[i-1]*29,h[i]=h[i-1]*29+s[i]-'a'+1;
11     for(int i=n;i;--i){
12         f[i]=f[i+1]+1;
13         while(M.count(hsh(i,i+f[i]-2))==0&&M.count(hsh(i+1,i+f[i]-1))==0)
14             f[i]--,upd(i+f[i]);
15         ans=max(ans,f[i]);
16     }cout<<ans<<endl;
17 }
View Code

正解稍微优雅一些:首先$SA$一下就可以求出$lcp$

然后我们求$f$的时候要查询的就是$rk$在一定区间内的数中最大的$f$

$O(nlogn)$

 

posted @ 2020-05-02 17:24  DeepinC  阅读(264)  评论(0编辑  收藏  举报