zoukankan      html  css  js  c++  java
  • 6834. 2020.10.24【NOIP提高A组】T4.onmyodo

    长度为(n),字符集为(K)的字符串,两个人博弈:每次要么删去一个字符,要么重新排列(要求排列后的字符串之前没有出现过)。

    问先手必胜的字符串的方案数。

    (nle 2*10^5,Kle 26)


    分析一波:

    假如字符串的排列数为偶数:

    1. 如果存在删去一个字符使得先手必胜,则先手必胜。
    2. 否则,先手选择重排。后手为了不输也选择重排。循环下去,最后后手不得不删去一个字符。

    于是,当字符串排列数为偶数时先手必胜。

    假如字符串的排列数为奇数:

    1. 如果先手选择重排,那就会变成字符串排列数为偶数的情况,后手必胜。
    2. 先手一定选择删去一个字符。如果删去字符之后排列数为偶数,则后手必胜;所以先手希望删去一个字符使得排列数为奇数。以下证明一定存在删去一个字符使排列数为奇数:(n=sum a_i),排列数为(frac{n!}{prod a_i!}),删去字符(i)相当于乘(frac{a_i}{n})。找到(lowbit(a_i))最小的(a_i),显然(lowbit(n)ge lowbit(a_i)),所以(2)的指数不会增加。

    于是,当字符串排列数为奇数时,如果(2|n)则后手必胜,否则先手必胜。

    综上,发现后手必胜的充要条件为:(2|n)且字符串排列数为奇数。

    (n!)(2)的指数为(sum_xlfloorfrac{n}{2^x} floor)(prod a_i!)(2)的指数为(sum_xsum_i lfloorfrac{a_i}{2^x} floor),现在我们希望它们相等。

    显然对于任意(x),有(lfloorfrac{n}{2^x} floor ge sum_i lfloorfrac{a_i}{2^x} floor)。为了满足上式,左式必须要取等号。

    差分一下得(lfloorfrac{n}{2^x} floor -lfloorfrac{n}{2^{x+1}} floor= sum_i lfloorfrac{a_i}{2^x} floor - lfloorfrac{a_i}{2^{x+1}} floor)

    也就是说(sum a_i)不进位。

    (库默尔定理??)

    然后直接DP:(f_{i,j})表示用了(i)个字符,出现次数或起来为(j)的方案数。转移的时候钦定新的字符必须选没有补的位中最小的那位。

    由于转移数不多,所以可以过。


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 250005
    #define ll long long
    #define mo 1000000009
    #define lowbit(x) ((x)&-(x))
    ll qpow(ll x,ll y=mo-2){
    	ll r=1;
    	for (;y;y>>=1,x=x*x%mo)
    		if (y&1)
    			r=r*x%mo;
    	return r;
    }
    ll fac[N],ifac[N];
    void initC(int n){
    	fac[0]=1;
    	for (int i=1;i<=n;++i)
    		fac[i]=fac[i-1]*i%mo;
    	ifac[n]=qpow(fac[n]);
    	for (int i=n-1;i>=0;--i)
    		ifac[i]=ifac[i+1]*(i+1)%mo;
    }
    ll C(int m,int n){return fac[m]*ifac[n]%mo*ifac[m-n]%mo;}
    int n,K;
    ll f[20][N];
    int main(){
    //	freopen("in.txt","r",stdin);
    	freopen("onmyodo.in","r",stdin);
    	freopen("onmyodo.out","w",stdout);
    	scanf("%d%d",&n,&K);
    	if (n&1){
    		printf("%lld
    ",qpow(K,n));
    		return 0;
    	}
    	f[0][0]=1;
    	initC(max(n,K));
    	for (int i=0;1<<i+1<=n && i+1<=K;++i){
    		for (int j=0;j<n;++j)
    			if (f[i][j]){
    				int t=lowbit(n^j);
    				for (int k=n^j^t;1;k=(k-1)&(n^j^t)){
    					int s=k^t;
    					(f[i+1][j^s]+=f[i][j]*ifac[s])%=mo;
    					if (k==0) break;
    				}
    			}
    	}
    	ll ans=0;
    	for (int i=0;1<<i<=n && i<=K;++i)
    		(ans+=fac[n]*f[i][n]%mo*fac[K]%mo*ifac[K-i])%=mo;
    	printf("%lld
    ",(qpow(K,n)-ans+mo)%mo);
    	return 0;
    }
    
  • 相关阅读:
    jvisualm 结合 visualGC 进行jvm监控,并分析垃圾回收
    linux 查看服务器cpu 与内存配置
    arthas 使用总结
    selinux contexts 安全上下文的临时更改
    Android 8.1 Doze模式分析(五) Doze白名单及Debug方式
    Window 任意窗口置顶软件Window TopMost Control
    Android ApkToolPlus一个可视化的跨平台 apk 分析工具
    SVN Please execute the 'Cleanup' command.
    Android 如何在64位安卓系统中使用32位SO库
    Android cmd命令查看apk是32位还是64位?
  • 原文地址:https://www.cnblogs.com/jz-597/p/13873192.html
Copyright © 2011-2022 走看看