zoukankan      html  css  js  c++  java
  • YCH的模拟赛 T2

    首先这个期望就是吓唬人的,因为输出格式把期望消掉了

    其实就是求总的步数

    很容易想到如果通过连边的方式,会出现很多环,每一个环的长度(l_i)就表示这个环里面任意一个点经过(l_i)次变换可以变回它自己

    先看一种做法:

    假设每一种循环节长度都用到了

    我们设每一个环的长度(l_i),个数(n_i)

    假如每一个环里面都有至少一个点被选择了,那么答案就是(lcm(l_1...l_n))

    每一位有(n_1+...+n_k)种填法

    (k)表示长度不同的环的个数

    一共((n_1+...+n_k)^n imes lcm(l_1...l_m))

    这个算法对不对?

    其实不对。这种方法算出来的答案会大很多

    因为这样算,有可能会有没有用到的环

    那是不是就需要容斥来消掉不合法的?

    怎么容斥?

    假设不选(l_1),每一位有(n_2+...+n_k)种填法,一共有((n_2+...+n_k)^n)种选法

    假设不选(l_2),每一位有(n_1+n_3+...+n_k)种填法,一共有((n_1+n_3+...+n_k)^n)种选法

    其他的也是同理,最后把这些不选一种的情况减掉。

    发现又减多了,再把不选三种的加上,把不选四种的减去...

    这样就能算出k种循环节都被选了的方案数,然后乘上他们的lcm

    那么最后的做法就是 状压+容斥

    2^k枚举选择哪些循环节长度的字符,把他们都加起来

    那么k的范围是多少?

    (kleq 14)

    因为(1+...+14>100)

    总复杂度(O(3^k))

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    
    using namespace std;
    
    #define inc(a,b) {a+=b;if(a>=mo) a-=mo;}
    #define dec(a,b) {a-=b;if(a<0) a+=mo;}
    
    const int maxn=110;
    const int mo=1000000007;
    
    int n,m,y[maxn],z[maxn][2],cnt[maxn];
    
    bool vis[maxn];
    
    int gcd(int a,int b)
    {
    	if(!b) return a;
    	else return gcd(b,a%b);
    }
    
    int mul(int a,int b)
    {
    	if(b==0) return 1;
    	int ans=1;
    	while (b)
    	{
    		if(b&1) ans=1ll*ans*a%mo;
    		a=1ll*a*a%mo;
    		b>>=1;
    	}
    	return ans;
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int a=1;a<=m;a++)
    		scanf("%d",&y[a]);
    	for(int a=1;a<=m;a++)
    		if(!vis[a])
    		{
    			int p=y[a],num=1;
    			while (p!=a)
    			{
    				vis[p]=true;
    				p=y[p];
    				num++;
    			}
    			vis[a]=true;
    			cnt[num]+=num;
    		}
    	int q=0;
    	for(int a=1;a<=m;a++)
    		if(cnt[a])
    		{
    			z[q][0]=a;
    			z[q][1]=cnt[a];
    			q++;
    		}
    	int ans=0;
    	for(int a=1;a<(1<<q);a++)
    	{
    		int num1=0,lcm=1,v=0;
    		for (int b=0;b<q;b++)
    			if (a&(1<<b)) 
    			{
    				num1++;
    				lcm=lcm*z[b][0]/gcd(lcm,z[b][0]);
    			}
    		for(int b=a;b;b=(b-1)&a)
    		{
    			int num2=0,lcm=1,total=0;
    			for(int c=0;c<q;c++)
    				if(b&(1<<c))
    				{
    					num2++;
    					lcm=lcm*z[c][0]/gcd(lcm,z[c][0]);
    					total+=z[c][1];
    				}
    			if((num1&1)!=(num2&1)) dec(v,mul(total,n))
    			else inc(v,mul(total,n));
    		}
    		inc(ans,1ll*v*lcm%mo);
    	}
    	printf("%d
    ",ans);
    
    	return 0;
    }
    
  • 相关阅读:
    知识加油站
    markdown 使用
    今日计划
    继续继续
    我回来了
    2020/5/8
    2020/5/10
    明天一定要学习了
    入驻博客园
    this is a test
  • 原文地址:https://www.cnblogs.com/lcezych/p/13266811.html
Copyright © 2011-2022 走看看