CF838C-Future Failure【dp,子集卷积】

1|0正题

题目链接:https://www.luogu.com.cn/problem/CF838C


1|1题目大意

一个字符串s,两个人轮流操作,每次每个人可以选择删掉一个字符或者重排列这个字符串,但是不能出现之前出现过的字符串,不能操作者输。

求有多少个长度为n且字符集大小为k的字符串使得先手必胜。

1n250000,1k26


1|2解题思路

显然如果删掉一个字符能使得先手必败,那么先手必胜。如果不能,那么肯定会一直重排列这个字符串。

那么如果一个字符串的排列数是偶数,那么先手可以切换先后手,所以先手必胜。否则先手需要考虑能否删除一个字符使得先手必败。

设第i个字符的数量是ai,那么一个字符串可重排列的方案就是n!i=1kai!,并且如果删除一个字符i,那么排列方式将会乘上ain

那么如果n是奇数,肯定存在一个ai是奇数,也就是说删除一个i后排列方式的奇偶性不变。所以如果一个n是奇数且字符串的排列数是奇数,那么肯定可以删除一个字符使得排列数仍然是偶数。
那么如果n是奇数且先手,那么肯定不会被逼到一个走动后先手必胜的位置,所以n是奇数先手必胜。

然后如果n是偶数,那么如果排列方式是偶数那么先手必胜否则先手必败。

然后考虑怎么计数n是偶数的情况。考虑到一个n!包含的2质因数的个数为i=0n2i,那么如果一个字符串的排列方式是偶数那么肯定有

i=0n2i=p=1ki=0ap2i

然后又因为假设我们考虑一直分解x出来,显然i=1kai!x肯定不会比n!x多,同理分解2k出来也是一样的,所以它们每一个i求出来的答案都是恰好相等的,即

iN,n2i=p=1kap2i

那么ai求和的时候二进制就不能有进位了,也就是说对于n中的每个1ai都恰好有一个是1n中的每一个0ai这一位都是0

也就是把n的二进制分成若干份ai,每一份的贡献是1ai!,要求贡献的乘积和。这个分出一个部分来的转移其实就是子集卷积,所以我们跑k次子集卷积即可。

这样跑有点慢,所以我们还需要快速幂优化。

时间复杂度:O(klogknlog2n)


1|3code

#pragma GCC optimize(2) %:pragma GCC optimize(3) %:pragma GCC optimize("Ofast") %:pragma GCC optimize("inline") #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #include<vector> #define mp(x,y) make_pair(x,y) #define ll long long using namespace std; const ll N=1e6+10; struct node{ ll to,next; }a[N]; ll n,m,tot,ls[N],dep[N],len[N],son[N]; ll d[N],s[N],g[N],*f[N],*now,ans[N]; vector<pair<ll,ll> >q[N]; ll read() { ll x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-f;c=getchar();} while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar(); return x*f; } void print(ll x){ if(x>9)print(x/10);putchar(x%10+48);return; } void addl(ll x,ll y){ a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot;return; } void dfs(ll x){ for(ll i=ls[x];i;i=a[i].next){ ll y=a[i].to;dfs(y); if(len[y]>=len[son[x]])son[x]=y; } if(son[x])len[x]=len[son[x]]+1; return; } void solve(ll x){ if(son[x]){ f[son[x]]=f[x]+1; solve(son[x]); d[x]=d[son[x]]; s[x]=s[son[x]]-d[x]; f[x][0]-=s[x]; } d[x]++;s[x]++; for(ll i=ls[x];i;i=a[i].next){ ll y=a[i].to; if(y==son[x])continue; f[y]=now;now+=len[y]+1;solve(y); int r=f[y][len[y]]+s[y]+d[y]*len[y]; s[x]+=r;f[x][0]-=r; for(ll j=0;j<=len[y];j++) f[x][j+1]+=f[y][j]+s[y]+d[y]*j-r; } for(ll i=0;i<q[x].size();i++){ ll p=min(q[x][i].first,len[x]); ans[q[x][i].second]=f[x][p]+s[x]+d[x]*p; } return; } signed main() { n=read();m=read(); for(ll i=2,x;i<=n;i++) x=read(),addl(x,i); for(ll i=1;i<=m;i++){ ll x=read(),d=read(); q[x].push_back(mp(d,i)); } dfs(1);f[1]=g;now=g;now+=len[1]+1; solve(1); for(ll i=1;i<=m;i++) print(ans[i]),putchar('\n'); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16084426.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(74)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-03-31 P5666-[CSP-S2019]树的重心【树状数组】
2021-03-31 CF891E-Lust【EGF】
2021-03-31 POJ3734-Blocks【EGF】
2021-03-31 P4494-[HAOI2018]反色游戏【圆方树】
2021-03-31 P7323-[WC2021]括号路径【并查集,启发式合并】
2021-03-31 P4199-万径人踪灭【FFT】
2021-03-31 P4640-[BJWC2008]王之财宝【OGF,Lucas定理】
点击右上角即可分享
微信分享提示