暑期集训Day1总结(组合数学&&概率期望)

总结一下今天所学的重点知识,选几道今天的练习题做代表

A - 整数分解为2的幂(51nod 1383)

基础计数问题

奇数时,num[i]=num[i-1] (好理解,就是前一个每个式子都加上1)

偶数时,num[i]=num[i-1]+num[i/2] (课件上说的思想,分解子问题)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll num[1000010];
const int mod=1000000007;
int main()
{
	int n;
	scanf("%d",&n);
	num[1]=1;
	num[2]=2;
	for(int i=3;i<=n;i++)
	{
		if(i%2==0)
		{
			num[i]=(num[i-1]+num[i/2])%mod;
		}
		else
		{
			num[i]=num[i-1];
		}
	}
	printf("%lld\n",num[n]);
	return 0;
 } 

B - Permutations(poj2369)

考查置换

对于每个数,求一遍置换的循环节长度,将他们取最小公倍数即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int p[1010];
ll gcd(ll a, ll b)
{
    return b ? gcd(b,a%b):a;
}
ll g(ll a,ll b)
{
	return a*b/gcd(a,b);
}
int main()
{
	int n;
    scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&p[i]);
	ll cnt,ans,cur;
	ans=1;
	for(int i=1;i<=n;i++)
	{
		cur=p[i];
		cur=p[cur];
		cnt=1;
		while(1)
		{
			if(cur==p[i])
			break;
			else
			{
			    cnt++;
				cur=p[cur];
			}
		}
//		printf("%lld\n",ans);
		ans=g(cnt,ans);
	}
	printf("%lld\n",ans);
	return 0;
} 

C - Invoker (hdu3923)

polya计数

用课堂上讲的规律即可,分两种情况,旋转和翻转。

具体情况的话可以参考https://blog.csdn.net/a601025382s/article/details/12109847

值得注意的是,最后一步ans*modReverse(2*n,mod)%mod;

因为是模运算,不能直接除2*n,那就求一下逆元了(方法可自行百度)

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod=1000000007;
long long extendGcd(long long a, long long b, long long &x, long long &y)
{
    if (a == 0 && b == 0)
    {
        return -1;  
    }
    if (b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    long long d = extendGcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
long long modReverse(long long a, long long n)
{
    long long x, y;
    long long d = extendGcd(a, n, x, y);
    if (d == 1)
    {
        return (x % n + n) % n;
    }
    else
    {
        return -1; 
    }
}
ll gcd(ll a,ll b)
{
   return b==0?a:gcd(b,a%b);
}
ll powerMod(ll x,ll n,ll m)
{
	ll res=1;
	while(n>0)
	{
		if(n&1)
		res=(res*x)%m;
		x=(x*x)%m;
		n>>=1;
	}
	return res;
}
ll polya(ll m,ll n)
{
	ll ans=0;
	for(int i=1;i<=n;i++)
	ans=(ans+powerMod(m,gcd(i,n),mod))%mod;
	if(n%2==1)
	{
		ans=(ans+n*powerMod(m,n/2+1,mod))%mod;
	}
	else
    {
    	ans=(ans+n/2*powerMod(m,n/2+1,mod)+n/2*powerMod(m,n/2,mod))%mod;
	}
	return ans*modReverse(2*n,mod)%mod;
}
int main()
{
	int t;
	ll n,m;
	int cnt=1;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld%lld",&m,&n);
		ll res=polya(m,n);
		printf("Case #%d: %lld\n",cnt,res);
		cnt++;
	}
	return 0;
 } 

D - Aeroplane chess(hdu4405)

期望/概率dp

从后往前递推即可(经常是这样推),遇到飞行区间不加1次的投骰子次数。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int fly[100011];
double p[100011];
int main()
{
	int n,m,u,e;
	while(1)
	{
		scanf("%d%d",&n,&m);
		if(!n&&!m)
		break;
		memset(fly,0,sizeof(fly));
		memset(p,0,sizeof(p));
		int u=0;
		for(int i=1;i<=m;i++)
		{
		   scanf("%d%d",&u,&e);
		   fly[u]=e;
	    }
	    for(int i=n-1;i>=0;i--)
	    {
	    	if(fly[i])
	    	{
	    		p[i]=p[fly[i]];
			}
			else
			{
				for(int j=1;j<=6;j++)
				p[i]+=(p[i+j]+1)/6;
			}
		}
		printf("%.4lf\n",p[0]);
	}
	return 0;
}

E - 有趣的数列(bzoj1485)

卡特兰数(当然我没看出来)

可以看看hzwer大神的博客 hzwer.com/5769.html,讲的很详细。

值得一提的是这道题hzwer算C(2*n,n)方法很好,可以细看看。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
bool is_prime[2000010];
int prime[2000010];
int minn[2000010];
int num[2000010];
int n;
int sieve()
{
	int rec=0;
	for(int i=2;i<=2*n;i++)
	{
		is_prime[i]=true;
	}
	for(int i=2;i<=2*n;i++)
	{
		if(is_prime[i])
		{
			prime[++rec]=i;
			minn[i]=rec;
		}
		for(int j=1;i*prime[j]<=2*n&&j<=rec;j++)
		{
			is_prime[i*prime[j]]=false;
			minn[i*prime[j]]=j;
			if (i%prime[j]==0) break;
		}
	}
	return rec;
}
void add(int x,int v)
{
	while(x!=1)
	{
		num[minn[x]]+=v;
		x/=prime[minn[x]];
	}
}
int main()
{
	int p;
	scanf("%d%d",&n,&p);
	int cnt=sieve();
//	for(int i=1;i<=cnt;i++)
//	printf("%d\n",prime[i]);
//    printf("%d\n",minn[5]);
	for(int i=2*n;i>n;i--)
	add(i,1);
	for(int i=1;i<=n;i++)
	add(i,-1);
	add(n+1,-1);
	ll ans=1;
	for(int i=1;i<=cnt;i++)
	{
		while(num[i]--)
		ans=(ans*prime[i])%p;
	}
	printf("%lld\n",ans);
	return 0;
}

F - Square-free integers (spoj4168)

用莫比乌斯函数来容斥

把每一个数写成p^2*q的形式,其中p是最大的能构成平方的因子,这样表示使得每一个数都是唯一的。
假设p=2,那么考虑1-n内有多少个可以写成2^2*q的数,是n/2/2个,
这其中也包括p!=2的情况,但是可以通过容斥把这些情况减去。
而加减的系数是通过mobius函数决定的,
于是通过枚举p,最后的答案是sum(n/p/p*mobius[p]);(摘录自某大神博客)

接着根据莫比乌斯函数定义来求莫比乌斯函数。(可参考上课课件)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int mob[10000010];
int prime[10000010];
bool is_prime[10000010];
void get_mob()
{
	mob[1]=1;
	int tot=0;
	for(int i=2;i<10000010;i++)
	is_prime[i]=true;
	for(int i=2;i<=10000000;i++)
	{
		if(is_prime[i])
		{
			mob[i]=-1;
			prime[++tot]=i;
		}
		for(int j=1;j<=tot&&i*prime[j]<=10000000;j++)
		{
			is_prime[i*prime[j]]=false;
			if(i%prime[j]==0)
			mob[i*prime[j]]=0;
			else
			mob[i*prime[j]]=-1*mob[i];
		}
	}
}
int main()
{
	int t;
	ll n;
	get_mob();
	scanf("%d",&t); 
	while(t--)
	{
		scanf("%lld",&n);
		ll ans=0;
		for(ll i=1;i*i<=n;i++)
		ans+=mob[i]*n/(i*i);
		printf("%lld\n",ans); 
	}
	return 0;
}

G - Sky Code(poj3904)

还是莫比乌斯函数+容斥,和上题差不多思路,用到了公式 F(d)=∑d∣x f(x)F(d)=∑d∣x f(x),则f(d)=∑d∣x μ(x/d)F(x)

会发现求莫比乌斯函数是一样的

可参考:https://blog.csdn.net/danliwoo/article/details/48933157

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int mob[10010];
int prime[10010];
bool is_prime[10010];
int a[10010];
int num[10010]; 
void get_num(int x)  //计算x的因子个数 
{
	int len=sqrt(x);
	for(int i=1;i<=len;i++) 
	{
	     if(x%i==0)
       {
          num[i]++;
          num[x/i]++;
       }
    }
    if(len*len==x)
    num[len]--;
}
void get_mob()
{
	mob[1]=1;
	int tot=0;
	for(int i=2;i<=10000;i++)
	is_prime[i]=true;
	for(int i=2;i<=10000;i++)
	{
		if(is_prime[i])
		{
			mob[i]=-1;
			prime[++tot]=i;
		}
		for(int j=1;j<=tot&&i*prime[j]<=10000;j++)
		{
			is_prime[i*prime[j]]=false;
			if(i%prime[j]==0)
			{
			   mob[i*prime[j]]=0;
			   break;
		    }
			else
			mob[i*prime[j]]=-1*mob[i];
		}
	}
}
ll cal(ll n)  //C(n,4)
{
	return n*(n-1)*(n-2)*(n-3)/24;
}
int main()
{
	get_mob();
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		memset(num,0,sizeof(num));
		for(int i=1;i<=n;i++)
		{
		   scanf("%d",&a[i]);
		   get_num(a[i]);
	    }
		ll ans=0;
		if(n<4)
		printf("0\n");
		else
	   {
		for(int i=1;i<=10000;i++)
		{
		   if(num[i]<4)
		   continue;
		   ans+=mob[i]*cal(num[i]);
	    }
		printf("%lld\n",ans);
	   }
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/star_moon0309/article/details/81348643