容斥原理的应用(初步)
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];
------------------------------------------------------------------
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) )
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()算的就是有序时的问题。这样就把无序转成有序了。
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