容斥原理的应用(初步)

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

2010111522290791.png2010111522292838.png

表示以上为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];

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

ContractedBlock.gif ExpandedBlockStart.gif 代码
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) ) 

ContractedBlock.gif ExpandedBlockStart.gif 代码
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()算的就是有序时的问题。这样就把无序转成有序了。

ContractedBlock.gif ExpandedBlockStart.gif 代码
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

转载于:https://www.cnblogs.com/LitIce/archive/2010/11/15/1878016.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值