菜鸟就要老老实实重新学起:
容斥原理:
容斥原理就是求有重复覆盖区间的总和大小,
eg:
//大小为n1,n2,n3的三个集合,有多少种组合
//二集合中第i个不能与a[i]个一集合的项组合,不能与b[i]个三集合的项组合
int a[N],b[N];
long long solve(int n2,int n2,int n3)
{
long long sum=n1*n2*n3;
for(int i=1;i<=n2;i++)
{
sum-=a[i]*n3;
sum-=b[i]*n1;
sum+=(a[i]*b[i]);//容斥原理
}
return sum;
}
就是把每个区间的大小加到总和上,然后把每两个区间的相交部分减去,把每三个区间的相交部分加上,如此处理……
所以通常深搜比较好写,设置falg奇数时加上当前值,偶数时减去当前值,重点就是如何让求每一轮的值,注意不要重复,
深搜方向的选择,或者状压存状态,如此,如此……
eg:
HDU1796 How many integers can you find
http://acm.hdu.edu.cn/showproblem.php?pid=1796
题意:
求小于n的数中能够整除a数组中某个数的数有多少个。
思路:
可以简单求出数组中每个数在小于n的数中的答案个数,就是(n-1)/a[i],之后相加就是全部的数,但是有重复,所以要减去同时是两个数的倍数的数,
eg:a[ ] = {2,3}; sum=(n-1)/2+(n-1)/3-(n-1)/(lcm(2,3));
然而当a数组中的数多余两个时,减去的数中也会有重复,所以再减去同时是三个数的倍数的数,如此……就是容斥原理了。
code:
int n,m;
long long sum;
int a[21];
int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
int lcm(int x,int y)
{
return x/gcd(x,y)*y;
}
void dfs(int now,int step,int flag)
{
sum+=(n/step)*flag;
for(int i=now+1;i<m;i++)
dfs(i,lcm(max(step,a[i]),min(step,a[i])),-flag);
}
int main()
{
int i,j,k,kk,t,x,y,z;
while(scanf("%d%d",&n,&m)!=EOF&&n)
{
n--;
for(i=0,j=0;i<m;i++)
{
scanf("%d",&a[i]);
if(a[i])a[j++]=a[i];
}
m-=(i-j);
sum=0;
for(i=0;i<m;i++)
dfs(i,a[i],1);
printf("%lld\n",sum);
}
return 0;
}
POJ3695 Rectangles
http://poj.org/problem?id=3695
题意:
给出许多矩形的坐标,求每个询问中的矩形覆盖的面积大小。
思路:
看到有人用线段树扫面线过的,然而只有最多20个矩形这道题写容斥要方便得多。
离线处理,状压存下每个问题所包含的 矩形,
dfs扫到所有情况,当某个问题包含该情况时,通过flag判断包含的矩形是奇数还是偶数加减到询问答案上。
code:
#define N 112345
int n,m;
int flag,sum,ave,ans,res,len,ans1,ans2;
int a[N],b[N];
struct node
{
int x,y;
int xx,yy;
}tn[N];
void dfs(int x,int y,int xx,int yy,int now,int step,int flag)
{
if(x>=xx||y>=yy)
return ;
if(now==n)
{
if(step)
for(int i=0;i<m;i++)
if((a[i]|step)==a[i])
b[i]+=flag*(xx-x)*(yy-y);
return;
}
dfs(x,y,xx,yy,now+1,step,flag);
dfs(max(x,tn[now].x),max(y,tn[now].y),min(xx,tn[now].xx),min(yy,tn[now].yy),now+1,step|(1<<now),-flag);
}
int main()
{
int i,j,k,kk,t,x,y,z;
kk=0;
while(scanf("%d%d",&n,&m)!=EOF&&n)
{
for(i=0;i<n;i++)
scanf("%d%d%d%d",&tn[i].x,&tn[i].y,&tn[i].xx,&tn[i].yy);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(i=0;i<m;i++)
{
scanf("%d",&x);
for(j=0;j<x;j++)
{
scanf("%d",&t);
a[i]|=(1<<(t-1));
}
}
dfs(0,0,INF,INF,0,0,-1);
printf("Case %d:\n",++kk);
for(i=0;i<m;i++)
printf("Query %d: %d\n",i+1,b[i]);
printf("\n");
}
return 0;
}