总结一下今天所学的重点知识,选几道今天的练习题做代表
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;
}