poj3695&bzoj1853 容斥定理

容斥定理的几个实际应用:
1、求几个矩形的面积并 题目poj3695
2、求一个区间【l,r】内是a1、a2、—-an倍数的数的总数 题目bzoj 1853
3、求一个区间[l,r]与m互质的数的个数
(第三点区间个数为r-l+1,先将m分解质因数a1,a2—an个,求出区间[l,r]内是a1,a2—an倍数的数的总数sum,r-l+1-sum即所与m互质的数的总数) 题目poj1091

poj3695
题意:给出n个矩形,每次询问若干个矩阵的面积并。
在这里由于线段树太伤了,所以就不用这个方法了~~改用面积并。
遍历一次并操作,维护符合的询问即可

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <fstream>
#include <iostream>
#include <algorithm>
using namespace std;
#define exp 1e-8
#define INF 0x3f3f3f3f
#define ll long long
#define set(a,b) memset(a,b,sizeof(a));
#define set(a,b) memset(a,b,sizeof(a));
#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)
#define for0(a,b) for(int a=0;a<=b;a++)//0---(b)
void bug(string st="bug")
{cout<<st<<endl;}
template<typename __ll>
inline void READ(__ll &m){
    __ll x=0,f=1;char ch=getchar();
    while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    m=x*f;
}
template<typename __ll>
inline void read(__ll &m){READ(m);}
template<typename __ll>
inline void read(__ll &m,__ll &a){READ(m);READ(a);}
template<typename __ll>
inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}
struct dat
{
    int x1,y1,x2,y2;
}d[22];
int s[1111111+10];
int aks[1111111+10];
int n,m,cmd,k;
void dfs(int x1=0,int y1=0,int x2=10001,int y2=10001,int deep=0,int flag=-1,int sta=0)
{
    if(x1>=x2||y1>=y2)return ;
    if(deep==n)
    {
        if(sta) //选了
        {
            for1(i,m)
                if((sta|aks[i])==aks[i])
                    s[aks[i]]+=flag*(x2-x1)*(y2-y1);
        }
        return ;
    }
    dfs(x1,y1,x2,y2,deep+1,flag,sta);
    dfs(max(x1,d[deep+1].x1),max(y1,d[deep+1].y1),min(x2,d[deep+1].x2),min(y2,d[deep+1].y2),deep+1,-flag,sta|(1<<deep));
}
int main()
{
    int cas=1;
    while(scanf("%d %d",&n,&m),n+m)
    {
        set(aks,0);
        set(s,0);
        for1(i,n)read(d[i].x1,d[i].y1),read(d[i].x2,d[i].y2);
        for1(i,m)
        {
            read(cmd);
            while(cmd--)
            {
                read(k);
                aks[i]|=(1<<(k-1));
            }
        }
        dfs();
        printf("Case %d:\n",cas++);
        for1(i,m)
            printf("Query %d: %d\n",i,s[aks[i]]);
        printf("\n");
    }
    return 0;
}

bzoj 1853
题意:求若干幸运数在一个区间的倍数的总数
很明显是容斥定理
这里要注意,生成的数先优化一下,去掉自相为倍数的数
遍历从大到小遍历,可以省去很多无用的遍历

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <fstream>
#include <iostream>
#include <algorithm>
using namespace std;
#define exp 1e-8
#define INF 10000000000
#define ll long long
#define ull unsigned long long int
#define set(a,b) memset(a,b,sizeof(a));
#define set(a,b) memset(a,b,sizeof(a));
#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)
#define for0(a,b) for(int a=0;a<=b;a++)//0---(b)
void bug(string st="bug")
{cout<<st<<endl;}
template<typename __ll>
inline void READ(__ll &m){
    __ll x=0,f=1;char ch=getchar();
    while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    m=x*f;
}
template<typename __ll>
inline void read(__ll &m){READ(m);}
template<typename __ll>
inline void read(__ll &m,__ll &a){READ(m);READ(a);}
template<typename __ll>
inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}
ull num[100000];
ull b[100000];
bool vis[100000];
int cnt=0;
int n=0;
ull ans=0;
ull l,r;
void dfs1(ull sum)
{
    if(sum>r)return;
    num[++cnt]=sum;
    dfs1(sum*10+6);
    dfs1(sum*10+8);
}
void dfs2(int deep=n,int flag=-1,ull sta=1)
{
    if(!deep)  //遍历完毕
    {
        if(sta!=1)  //如果选择了~~
            ans+=flag*(r/sta-(l-1)/sta);
        return ;
    }
    dfs2(deep-1,flag,sta);  //不选择
    ull tmp=sta/__gcd(sta,num[deep]);
    if((double)tmp*num[deep]<=r)  //加一个条件
        dfs2(deep-1,-flag,tmp*num[deep]);  //选择
}
int main()
{
    read(l,r);
    dfs1(6);
    dfs1(8);
    sort(num+1,num+1+cnt);
    for1(i,cnt)  //一个小小的优化,去掉同倍数的值
        if(!vis[i])
        {
            b[++n]=num[i];
            for(int j=i+1;j<=cnt;j++)
                if(num[j]%num[i]==0)
                    vis[j]=1;
        }
    for1(i,n)
        num[i]=b[i];
    dfs2();
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值