VJ 点击打开链接
参考 点击打开链接
非常好的译文:点击打开链接
容斥原理的想法就是求多个集合的并集.所以要先设计好集合.
组合数学问题中,正面解决会困难,常用方法是正难则反,使用容斥原理求反向在用全集减去.将对立面的限制条件分析清楚.
eg 求区间互质的数的个数,则用除法等计算出一个数的倍数的方法再减去.
BC25第2题想到的是容斥,独立的取考虑了行的情况与列的情况(而题解中有一个假设就是当行都满足的时候,即先考虑列的情况,然后那些行如何放,我深陷在了求得行,求得列再容斥就混乱了,容斥是单一集合)。分阶段一行一行放,对于每一行有操作,状态设定区分开。dp[i][j][k] 第i行,j列被占用,用了k个石子
UVa 11806 Cheerleaders
求k个石子放在n*m的矩阵里 并且第一行 最后一行 第一列 最后一列都要有石子
考虑反面 求出所有的 减去不满足的情况
容斥原理总共4个 集合A(第一行没有石子) B(最后行没有石子)C(第一列没有石子)D(最后一列没有石子)
减去1个集合的 加上2个集合的 减去3个集合的 加上4个集合的
#include <cstring>
#include <cstdio>
const int maxn = 510;
const int mod = 1000007;
int C[maxn][maxn];
int main()
{
C[0][0] = 1;
for(int i = 0; i <= 500; i++){
C[i][0] = C[i][i] = 1;
for(int j = 1; j < i; j++)
C[i][j] = (C[i-1][j] + C[i-1][j-1]) %mod;
}
int T;
int cas = 1;
scanf("%d", &T);
while(T--)
{
int n, m, k, sum = 0;
scanf("%d %d %d", &n, &m, &k);
for(int s = 0; s < 16; s++){
int b = 0, r = n, c = m;
if(s&1)
r--,b++;
if(s&2)
r--,b++;
if(s&4)
<span style="white-space:pre"> </span>c--,b++;
if(s&8)
c--,b++;
if(b&1)
sum = ((sum - C[r*c][k]) % mod + mod) % mod;
else
sum = (sum + C[r*c][k]) % mod;
}
printf("Case %d: %d\n", cas++, sum);
}
return 0;
}
HDU 1796 How many integers can you find
练习DFS式列举
#include<stdio.h>
#include<string.h>
int a[20], N, M, la ;
int hash[20];
__int64 gcd(__int64 a, __int64 b)
{
__int64 c ;
while(b)
{
c = a % b ;
a = b ;
b = c ;
}
return a ;
}
// 当前点 已经加入容斥的个数,记录容斥的过程值 结果
void dfs(int now, int count, __int64 lcm, __int64 &sum)
{
lcm = a[now] / gcd(lcm, a[now]) * lcm ;
if(count & 1)
sum += (N - 1) / lcm ;
else sum -= (N - 1) / lcm ;
for(int i = now + 1; i < M; i++)
dfs(i, count + 1, lcm, sum);
}
int main()
{
int b,i ;
while(scanf("%d%d", &N, &M) != EOF)
{
for(i = 0, la = 0; i < M; i++)
{
scanf("%d", &b);
if(b)
{
a[la++] = b ;
}
}
M = la ;
__int64 sum = 0 ;
//memset(hash,0,sizeof(hash));
for(i = 0; i < M; i++) //枚举起点
dfs(i, 1, a[i], sum);
printf("%I64d\n", sum);
}
return 0 ;
}
HDU 1695 GCD
在(a,b) ,(c,d) 区间内选两个数使得GCD(X,Y)=k 那么我们直接将区间范围/k,则求互质的数字即可,枚举时注意控制X<Y 就行.
/* ***********************************************
Author :bingone
Created Time :2015-1-26 11:47:59
File Name :HDU1685.cpp
************************************************ */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define eps 1e-8
#define inf 0x3f3f3f3f
#define maxn (10+100000)
#define zero(x) (fabs(x)<eps)
typedef long long ll;
using namespace std;
int N,M,T,CNT;
int num[maxn];
vector<int> yz[maxn];
ll ans;
void DEBUG(){
for(int i=0;i<maxn;i++){
cout<<i<<endl;
for(int j=0;j<yz[i].size();j++)
printf("%d ",yz[i][j]);
cout<<endl;
}
}
void init(){
memset(num,0,sizeof(num));
for(int i=0;i<maxn;i++) yz[i].clear();
for(int i=2;i<maxn;i++) if(num[i]==0){
for(int j=i;j<maxn;j+=i){
yz[j].push_back(i);
num[j]=1;
}
}
///DEBUG();
}
void dfs(int loc,int pos,int cnt,int num,int t){
if(pos>=yz[loc].size()) return;
if(cnt & 1) ans+=t-t/num;
else ans-=t-t/num;
for(int i=pos+1;i<yz[loc].size();i++)
dfs(loc,i,cnt+1,num*yz[loc][i],t);
}
int main()
{
//freopen("1.out","r",stdin);
//freopen("out.txt","w",stdout);
init();int cas=1;
scanf("%d",&T);
while(T--){
int a,b,c,d,k;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(k==0){printf("Case %d: 0\n",cas++);continue;}
if(b>d) swap(b,d);
b/=k;d/=k;ans=0;
if(b) ans=d;
for(int i=2;i<=b;i++)
for(int j=0;j<yz[i].size();j++)
dfs(i,j,1,yz[i][j],d-i);
printf("Case %d: %I64d\n",cas++,ans);
}
return 0;
}
HDU 2204 Eddy's爱好
任何一个数字被表示乘一个数的质数次幂是唯一的.M^K K位质数.还有就是两个质数的乘积这种情况减去,奇加偶减.
因为三个质数乘积2*3*7=42 2^42>10^18 所以就到三就满足了.
/* ***********************************************
Author :bingone
Created Time :2015-1-26 11:47:59
File Name :HDU1685.cpp
************************************************ */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define eps 1e-8
#define inf 0x3f3f3f3f
#define maxn (10+1000)
#define zero(x) (fabs(x)<eps)
typedef long long ll;
using namespace std;
int N,M,T,CNT;
int num[100];
int prim[100];
void init(){
memset(num,0,sizeof(num));
CNT=0;
for(int i=2;i<100;i++){
if(num[i]==0) prim[CNT++]=i;
for(int j=0;j<CNT;j++){
if(i*prim[j]>100) break;
num[i*prim[j]]=1;
if(i%prim[j]==0) break;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ll n;
init();
while(~scanf("%I64d",&n)){
ll ans=1;
for(int i=0;i<CNT;i++){
ll tmp=(ll)(pow((double)n,1./prim[i])+eps);
if(pow((double)tmp,prim[i])>n) tmp--;
if(tmp<2) break;
ans+=(ll)tmp-1;
}
for(int i=0;i<CNT;i++)
for(int j=i+1;j<CNT;j++){
int tt=prim[i]*prim[j];
ll tmp=(ll)(pow((double)n,1./tt)+eps);
if(pow((double)tmp,tt)>n) tmp--;
if(tmp<2) break;
ans-=(ll)tmp-1;
}
ll tmp=(ll)((double)pow(n,1./30)+eps);
if(pow((double)tmp,30)>n) tmp--;
if(tmp>=2) ans+=(ll)tmp-1;
tmp=(ll)(pow((double)n,1./42)+eps);
if(pow((double)tmp,42)>n) tmp--;
if(tmp>=2) ans+=(ll)tmp-1;
printf("%I64d\n",ans);
}
return 0;
}
HDU 4407
这个一样的道理,但是有个更新操作,规模1000,用map最后在计算下就OK了. 注意map 操作 ->的运算级别比较低
#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<set>
#include<string>
#include<queue>
#define inf 1<<28
#define M 100005
#define N 400005
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define pb(a) push_back(a)
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define MOD 1000000007
using namespace std;
map<int,int>mp;
map<int,int>::iterator it;
int n,q;
int prime[N][15]={0},flag[N]={0};
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
void Prime(){
for(int i=2;i<N;i++){
if(flag[i]){continue;}
prime[i][++prime[i][0]]=i;
for(int j=2;j*i<N;j++){
flag[i*j]=1;
prime[i*j][++prime[i*j][0]]=i;
}
}
}
LL ans;
void dfs(int idx,int num,int cnt,int m,int n,int p){
if(cnt==m){
int k=n/num;
if(m&1) ans-=(LL)num*k*(k+1)/2;
else ans+=(LL)num*k*(k+1)/2;
return ;
}
if(idx>prime[p][0]) return;
if(num>n) return ;
dfs(idx+1,num,cnt,m,n,p);
dfs(idx+1,num*prime[p][idx],cnt+1,m,n,p);
}
LL slove(int n,int p){
if(n<=0) return 0;
ans=(LL)n*(n+1)/2;
for(int i=1;i<=prime[p][0];i++){
// cout<<prime[p][i]<<endl;
dfs(1,1,0,i,n,p);
}
return ans;
}
int main(){
int t;
scanf("%d",&t);
Prime();
while(t--){
scanf("%d%d",&n,&q);
mp.clear();
while(q--){
int k,x,y,c;
scanf("%d",&k);
if(k==1){
scanf("%d%d%d",&x,&y,&c);
LL ret=slove(y,c)-slove(x-1,c);
for(it=mp.begin();it!=mp.end();it++){
if((*it).first>=x&&y>=(*it).first){
if(gcd(c,(*it).first)==1) ret-=(*it).first;
if(gcd(c,(*it).second)==1) ret+=(*it).second;
}
}
printf("%I64d\n",ret);;
}
else{
scanf("%d%d",&x,&y);
mp[x]=y;
}
}
}
return 0;
}
HDU 2841
还是乘法分解因子问题.
HDU 4135
这个是很基本的问题,不知道我的代码哪里错了,随机了1W组数据对拍都一样,无语.贴上我的,看哪位路过大神指点.
/* ***********************************************
Author :bingone
Created Time :2015-1-27 18:35:05
File Name :HDU4135.cpp
************************************************ */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define eps 1e-8
#define inf 0x3f3f3f3f
#define maxn (10+1000)
#define zero(x) (fabs(x)<eps)
typedef long long ll;
using namespace std;
int N,M,T;
ll A,B,NN,ans;
ll yz[1000005],yznum;
void dfs(int pos,int cnt,ll num){
if(pos>=yznum) return;
if(cnt & 1) ans+=B-B/num-A+A/num;
else ans-=B-B/num-A+A/num;
for(int i=pos+1;i<yznum;i++)
dfs(i,cnt+1,num*yz[i]);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%I64d%I64d%I64d",&A,&B,&NN);
A--;ans=yznum=0;
for(int i=2;i*i<=NN;i++) if(NN%i==0){
yz[yznum++]=i;
while(NN%i==0) NN/=i;
}
if(NN>1) yz[yznum++]=NN;
for(int i=0;i<yznum;i++)
dfs(i,1,yz[i]);
printf("Case #%d: %I64d\n",cas,ans);
}
return 0;
}
POJ 1091 跳蚤
求1...m中有多少个元组 满足aA+bB...+zZ=1 .正难则反.求不互质数
/* ***********************************************
Author :bingone
Created Time :2015-1-26 11:47:59
File Name :HDU1685.cpp
************************************************ */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define eps 1e-8
#define inf 0x3f3f3f3f
#define maxn (10+10000)
#define zero(x) (fabs(x)<eps)
typedef long long ll;
using namespace std;
int N,M,T,CNT;
int num[maxn];
int yz[100],yznum;
void getyz(){
yznum=0;int t=M;
for(int i=2;i*i<=t;i++) if(t%i==0){
yz[yznum++]=i;
while(t%i==0) t/=i;
}
if(t>1) yz[yznum++]=t;
}
int main()
{
while(~scanf("%d%d",&N,&M)){
getyz();
double ans=pow(M,N);
for(int i=1;i<(1<<yznum);i++){
int cnt=0, mul=1;
for(int j=0;j<yznum;j++) if(i&(1<<j))
cnt++,mul*=yz[j];
mul=M/mul;
if(cnt & 1) ans-=pow(mul,N);
else ans+=pow(mul,N);
}
printf("%.0f\n",ans);
}
return 0;
}