HDU 5514.Frogs-欧拉函数 or 容斥原理

Frogs

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 4904    Accepted Submission(s): 1631


Problem Description
There are  m stones lying on a circle, and n frogs are jumping over them.
The stones are numbered from 0 to m1 and the frogs are numbered from 1 to n. The i-th frog can jump over exactly ai stones in a single step, which means from stone j mod m to stone (j+ai) mod m (since all stones lie on a circle).

All frogs start their jump at stone 0, then each of them can jump as many steps as he wants. A frog will occupy a stone when he reach it, and he will keep jumping to occupy as much stones as possible. A stone is still considered ``occupied" after a frog jumped away.
They would like to know which stones can be occupied by at least one of them. Since there may be too many stones, the frogs only want to know the sum of those stones' identifiers.
 

 

Input
There are multiple test cases (no more than  20), and the first line contains an integer t,
meaning the total number of test cases.

For each test case, the first line contains two positive integer n and m - the number of frogs and stones respectively (1n104, 1m109).

The second line contains n integers a1,a2,,an, where ai denotes step length of the i-th frog (1ai109).
 

 

Output
For each test case, you should print first the identifier of the test case and then the sum of all occupied stones' identifiers.
 

 

Sample Input
3 2 12 9 10 3 60 22 33 66 9 96 81 40 48 32 64 16 96 42 72
 

 

Sample Output
Case #1: 42 Case #2: 1170 Case #3: 1872
 

 

Source
 

 

 

 

题意就是跳青蛙,通过分析会发现,就是步数a[i]与石头数m,通过gcd(a[i],m)之后,gcd的倍数的和。

因为重复的数只计算一次,所以要去重。

一开始想的是容斥去重,然而还是太捞了,。。。

 

这道题和队友讨论了3天,还问了学长,发现几个问题:

(1)如果直接枚举gcd的遍历,应该为去重他们的最小公倍数,也就是这样的。

for(ll j=0;j<cnt;j++)
            {
                if(i&(1<<j))
                    temp=temp*g[j]/gcd(temp,g[i]),jishu++;
            }

 

(2)直接gcd的容斥枚举去重会超时,因为极限数据可能要枚举1<<36次,for一次的极限数据个人认为可能就是1e7再带点常数,1<<36次跑不出来,程序会崩。所以这种容斥是不可以的,虽然想法真的很好,但是真的过不去。所以,最后放弃了这种思路,其实还是可以容斥的,但是是有技巧的容斥。

 

 

直接看的题解,所以也不好说什么,毕竟是人家的劳动成果,只是分析一下。

 

做法一:

欧拉函数的延伸用法:小于或等于n的数中,与n互质的数的总和为:φ(n) * n / 2  (n>1)。

做法二:

枚举m的因子个数,这样就会少很多,就不存在超时的问题了。

 

以上两种做法的具体题解传送门:HDU 5514 Frogs(欧拉函数+数论YY)

 

 

直接贴代码吧。

 

代码1(欧拉函数):

 1 //欧拉函数的公式求解
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<bitset>
 7 #include<cassert>
 8 #include<cctype>
 9 #include<cmath>
10 #include<cstdlib>
11 #include<ctime>
12 #include<deque>
13 #include<iomanip>
14 #include<list>
15 #include<map>
16 #include<queue>
17 #include<set>
18 #include<stack>
19 #include<vector>
20 using namespace std;
21 typedef long long ll;
22 typedef pair<int,int> pii;
23 
24 const double PI=acos(-1.0);
25 const double eps=1e-6;
26 const ll mod=1e9+7;
27 const int inf=0x3f3f3f3f;
28 const int maxn=1e5+10;
29 const int maxm=100+10;
30 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
31 
32 ll a[maxn],n,m;
33 
34 ll gcd(ll a,ll b)
35 {
36     return b==0?a:gcd(b,a%b);
37 }
38 
39 ll euler(ll n)
40 {
41     ll ans=n;
42     for(int i=2;i*i<=n;i++){
43         if(n%i==0){
44             ans=ans/i*(i-1);
45             while(n%i==0) n/=i;
46         }
47     }
48     if(n>1) ans=ans/n*(n-1);
49     return ans;
50 }
51 
52 bool solve(int x)
53 {
54     for(int i=0;i<n;i++){
55         if(x%a[i]==0) return true;
56     }
57     return false;
58 }
59 
60 int main()
61 {
62     int t;
63     scanf("%d",&t);
64     for(int cas=1;cas<=t;cas++){
65         memset(a,0,sizeof(a));
66         scanf("%lld%lld",&n,&m);
67         for(int i=0;i<n;i++){
68             scanf("%d",a+i);
69             a[i]=gcd(a[i],m);
70         }
71         ll ans=0;
72         for(int i=1;i*i<=m;i++){
73             if(m%i) continue;
74             if(solve(i)) ans+=(ll)euler(m/i)*m/2;
75             if(i*i==m||i==1) continue;
76             if(solve((m/i))) ans+=(ll)euler(i)*m/2;
77         }
78         printf("Case #%d: %lld\n",cas,ans);
79     }
80 }
View Code

 

代码2(容斥原理):

 1 //容斥定理
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<bitset>
 7 #include<cassert>
 8 #include<cctype>
 9 #include<cmath>
10 #include<cstdlib>
11 #include<ctime>
12 #include<deque>
13 #include<iomanip>
14 #include<list>
15 #include<map>
16 #include<queue>
17 #include<set>
18 #include<stack>
19 #include<vector>
20 using namespace std;
21 typedef long long ll;
22 typedef pair<int,int> pii;
23 
24 const double PI=acos(-1.0);
25 const double eps=1e-6;
26 const ll mod=1e9+7;
27 const int inf=0x3f3f3f3f;
28 const int maxn=1e5+10;
29 const int maxm=100+10;
30 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
31 
32 ll gcd(ll a,ll b)
33 {
34     return b==0?a:gcd(b,a%b);
35 }
36 
37 ll g[maxn],fac[maxn];
38 int tp[maxn],num[maxn],vis[maxn];
39 
40 int main()
41 {
42     int t;
43     scanf("%d",&t);
44     for(int cas=1;cas<=t;cas++){
45         ll n,m;
46         scanf("%lld%lld",&n,&m);
47         int ok=0;
48         for(int i=0;i<n;i++){
49             scanf("%lld",&g[i]);
50             g[i]=gcd(g[i],m);
51             if(g[i]==1) ok=1;
52         }
53         if(ok==1){
54             printf("Case #%d: %lld\n",cas,m*(m-1)/2);
55             continue;
56         }
57         sort(g,g+n);
58         n=unique(g,g+n)-g;
59         memset(vis,0,sizeof(vis));
60         memset(num,0,sizeof(num));
61         int cnt=0;
62         for(ll i=2;i*i<=m;i++){
63             if(i*i==m) fac[cnt++]=m/i;
64             else if(m%i==0) fac[cnt++]=i,fac[cnt++]=m/i;
65         }
66         sort(fac,fac+cnt);
67         int cnt1=0;
68         for(int i=0;i<n;i++){
69             if(!vis[i]){
70                 tp[cnt1++]=g[i];
71                 for(int j=0;j<n;j++)
72                     if(g[j]%g[i]==0) vis[j]=1;
73             }
74         }
75         memset(vis,0,sizeof(vis));
76         for(int i=0;i<cnt;i++){
77             for(int j=0;j<cnt1;j++){
78                 if(fac[i]%tp[j]==0){
79                     vis[i]=1;
80                     break;
81                 }
82             }
83         }
84         ll sum=0;
85         for(int i=0;i<cnt;i++){
86             if(num[i]!=vis[i]){
87                 sum+=m*(m/fac[i]-1)/2*(vis[i]-num[i]);
88                 for(int j=i+1;j<cnt;j++)
89                     if(fac[j]%fac[i]==0)
90                     num[j]=num[j]+vis[i]-num[i];
91             }
92         }
93         printf("Case #%d: %lld\n",cas,sum);
94     }
95     return 0;
96 }
View Code

 

贴一下我们想了3天的错误代码,纪念一下。

代码(错误的):

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<bitset>
 6 #include<cassert>
 7 #include<cctype>
 8 #include<cmath>
 9 #include<cstdlib>
10 #include<ctime>
11 #include<deque>
12 #include<iomanip>
13 #include<list>
14 #include<map>
15 #include<queue>
16 #include<set>
17 #include<stack>
18 #include<vector>
19 using namespace std;
20 typedef long long ll;
21 typedef pair<int,int> pii;
22 
23 const double PI=acos(-1.0);
24 const double eps=1e-6;
25 const ll mod=1e9+7;
26 const int inf=0x3f3f3f3f;
27 const int maxn=1e5+10;
28 const int maxm=100+10;
29 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
30 #define lson l,m,rt<<1
31 #define rson m+1,r,rt<<1|1
32 
33 ll gcd(ll a,ll b)
34 {
35     return b?gcd(b,a%b):a;
36 }
37 
38 ll sum(ll x,ll n)
39 {
40     ll temp=(n-1)/x;
41     return temp*x+(temp*(temp-1)/2)*x;
42 }
43 
44 ll a[maxn];
45 
46 int main()
47 {
48     ll t;
49     scanf("%lld",&t);
50     for(int cas=1;cas<=t;cas++)
51     {
52         ll n,m;
53         scanf("%lld%lld",&n,&m);
54         ll h=0;
55         for(int i=0;i<n;i++)
56         {
57             ll x;
58             scanf("%lld",&x);
59             a[h++]=gcd(x,m);
60         }
61         vector<ll> g;
62         sort(a,a+h);
63         for(int i=h-1;i>=0;i--){
64             int flag=0;
65             for(int j=i-1;j>=0;j--){
66                 if(a[i]%a[j]==0) flag=1;
67             }
68             if(!flag) g.push_back(a[i]);
69         }
70         int cnt=g.size();
71         ll ans=0;
72         for(ll i=0;i<(1ll<<cnt);i++)
73         {
74             ll temp=1,jishu=0;
75             for(ll j=0;j<cnt;j++)
76             {
77                 if(i&(1<<j))
78                     temp=temp*g[j]/gcd(temp,g[i]),jishu++;
79             }
80             if(jishu==0)continue;
81             if(jishu&1) ans+=sum(temp,m);
82             else ans-=sum(temp,m);
83         }
84         printf("Case #%d: %lld\n",cas,ans);
85     }
86 }
View Code

 

 

 

到此为止,拜拜,再也不看这个题了。

 

转载于:https://www.cnblogs.com/ZERO-/p/9813643.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值