现在的位置: 首页 > 综合 > 正文

容斥原理的应用(初步)

2012年10月02日 ⁄ 综合 ⁄ 共 3182字 ⁄ 字号 评论关闭

http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=2005

表示以上为oimaster的题解Orz。。。

Attention:

预处理的时间复杂度为O(nlog n)

枚举d  * "两次枚举“=sqrt(n)*(sqrt(n) * sqrt(m) ) ==O (n)

(d只要枚举到sqrt(n)就可以了,后面的都是一下就算好的)

时间复杂度仍和下面方法相同

再次Orz。。

做完POI07Zap这题,发现

加入问题求的不是每一个gcd=i (i=1..n)那么实际上预处理完就只要(sqrt(n) + sqrt(m) ) 就可以完成一次

询问!

这时候优势相当明显

一下代码不是以oimaster题解方法写的。。。

开始时cnt[i]表示以k*i为公约数的有多少个

DP从后向前  

i =n downto 1

cnt[i]-=cnt[i*k];k>=2   

Dp完cnt[i]就表示公约数为i个个数有多少个。

这样写可以对于一对(a,b)O(n*logn)求出所有的gcd=i的个数个数为cnt[i];

------------------------------------------------------------------

代码

1 #include<cstdio>
2 #include<cstring>
3
4 inline int min(int a,int b){return a<b?a:b;}
5
6  const int MAXN = 100010;
7
8  long long cnt[MAXN];
9
10  int main()
11 {
12 int n,m;
13 while(~scanf("%d%d",&n,&m))
14 {
15 int t = min(n,m);
16 for(int i=2;i<=t;i++)//gcd = i
17 cnt[i] = (long long)(n/i)*(m/i);//cnt[i] : the number of whose gcd is k*i
18
19 for(int i=t;i>=1;i--)
20 for(int k=2;k*i<=t;k++)
21 cnt[i]-=cnt[k*i];//to get the real number of whose gc is i
22 long long ans = 0;
23 for(int i=1;i<=t;i++)
24 ans+=2*(i-1)*cnt[i];
25 printf("%I64d\n",ans+(long long)n*m);
26 }
27 return 0;
28 }
29

-----------------------------------------------------------------------------

题意给定(a,b)求出1<=x<=a && 1<=y<=b && gcd(x,y)==k的对数

http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1101

题数据更恶心50000组数据50000*50000*log50000==TLE

所以就仔细Orz了oimaster的方法,然后去拜了WJB的代码,得到以下代码,方法就是上面的题解方法.

复杂度为(N log N + test*( sqrt(n)+sqrt(m) ) 

代码

1 #include <algorithm>
2 #include <cstdio>
3 #define rep(i,n) for(int i=0;i<n;i++)
4 const int maxn=50000+10;
5 using namespace std;
6 int P[maxn]={};
7 int get(int i)
8 {
9 int s=1;
10 for(int x=2;x*x<=i;x++)if(i%x==0)
11 {
12 i/=x;if(i%x==0)return 0;
13 s*=-1;
14 }
15 if(i>1)s*=-1;
16 return s;
17 }
18 int main()
19 {
20 for(int i=1;i<=maxn;i++)P[i]=P[i-1]+get(i);
21 int a,b,k,n;scanf("%d",&n);
22 rep(i,n)
23 {
24 scanf("%d%d%d",&a,&b,&k);a/=k;b/=k;
25 if(a>b)swap(a,b);
26 int ans=0;
27 for(int t=1;t<=a;t++)
28 {
29 int m=min(a/(a/t),b/(b/t))-t;
30 ans+=(P[t+m]-P[t-1])*(a/t)*(b/t);
31 t+=m;
32 }
33 printf("%d\n",ans);
34 }
35 return 0;
36 }
37

------------------------------------------------------------------------------

http://acm.hdu.edu.cn/showproblem.php?pid=1695

Yoiu can assume that a = c = 1 in all test cases.

要注意这句话,其他只要注意对数统计时时无序的!!

ans=calc(a,b)-(calc(a,a)+1)/2;

calc()算的就是有序时的问题。这样就把无序转成有序了。

代码

1 #include<iostream>
2 #include<cstdio>
3 #include<cmath>
4 #include<cstdlib>
5 #define LL long long
6 #define maxn 100010
7 using namespace std;
8
9 LL s,T,a,b,c,d,k,t1,t2,t3,t4,xx;
10 LL P[maxn];
11 LL t,dd,m;
12 LL gett(LL x)
13 {
14 s=1;
15 for (LL i=2;i*i<=x;++i)
16 if (x % i==0)
17 {
18 x/=i;
19 if (x % i==0) return 0;
20 s=s*(-1);
21 }
22 if (x>1) s*=-1;
23 return s;
24 }
25 LL calc_PRE(LL a,LL b)
26 {
27
28 t=0;
29 // a/=k;b/=k;
30 dd=min(a,b);
31 for (LL d=1;d<=dd;++d)
32 {
33 m=( min(a/(a/d),b/(b/d)) , d)-d;
34 t+=(P[d+m]-P[d-1])*(a/d)*(b/d);
35 d+=m;
36 }
37 return t;
38 }
39 LL calc(LL a,LL b)
40 {
41 if (a==0 || b==0) return 0;
42 if (a==b) return ( calc_PRE(a,a)+1 ) /2;
43 if (a>b) swap(a,b);
44 xx=calc_PRE(a,a);
45 return calc_PRE(a,b)-xx+ (xx+1)/2;
46 }
47 int main()
48 {
49 freopen("1695.in","r",stdin);
50 freopen("16955.out","w",stdout);
51 for (LL i=1;i<maxn;++i) P[i]=P[i-1]+gett(i);
52 cin>>T;
53 for (LL ii=1;ii<=T;++ii)
54 {
55 cin>>a>>b>>c>>d>>k;
56 if(k == 0)
57 {
58 printf("Case %I64d: 0\n",ii); continue;
59 }
60 if (b>d) swap(b,d);
61 b/=k;d/=k;
62
63 t4=calc(b,d);
64 // printf("%I64d\n%I64d\n%I64d\n%I64d\n",t1,t2,t3,t4);
65 printf("Case %I64d: %I64d\n",ii,t4);
66 }
67 // while (1);
68 return 0;
69 }
70

抱歉!评论已关闭.