HDU 5514 Frogs 【容斥原理】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Jane_JXR/article/details/78210839

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5514

题目大意:青蛙在编号为0~m-1的石头上跳跃,每次可以跳跃a[i]个位置,起始位置为0,问n次a[i]的跳跃之后,所经过的石头的编号总和。

对于第一组样例:当a[i]=9时,跳跃到的位置为0、9、6、3

                             当a[i]=10时,跳跃到的位置为10、8、6、4、2、0

重复跳跃的之计算一次,所以结果为9+6+3+10+8+4+2=42


分析:由于a[i]数据范围的限制,直接暴力模拟肯定会超时,我们可以发现,每次跳跃到的位置都是某个数的倍数,可以直接计算出这些数的和,但会有重复。我们可以从另一个角度考虑,找m的所有因子,然后标记能跳到的位置,对于当前位置,访问的次数等于标记的次数,不需要处理,其他情况容斥掉重复跳的或加上未跳的,求和即是结果。

CODE:

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<math.h>
#define INF 0x3f3f3f3f
#define lson l,m,root<<1
#define rson m+1,r,root<<1|1
typedef unsigned long long LL;
using namespace std;
const int maxn=100005;
int a[maxn],b[maxn];
int p[maxn];
int num[maxn],vis[maxn];
int main()
{
    int t,n,m,T=0;
    scanf("%d",&t);
    while(t--)
    {
        memset(p,0,sizeof(p));
        memset(vis,0,sizeof(vis));
        memset(num,0,sizeof(num));
        memset(b,0,sizeof(b));
        scanf("%d%d",&n,&m);
        int tot=0;
        for(int i=1; i<=sqrt(m); i++)//求m的所有因子
        {
            if(m%i==0)
            {
                p[tot++]=i;
                if(i*i!=m)
                    p[tot++]=m/i;
            }
        }
        sort(p,p+tot);
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            b[i]=__gcd(a[i],m);
            for(int j=0;j<tot;j++)//判断当前位置在因子中能否被访问到并标记
            {
                if(p[j]%b[i]==0)
                    vis[j]=1;//能被访问的设为1,即保证每个位置只访问一次
            }
        }
        tot--;
        LL sum=0;
        for(int i=0; i<tot; i++)
        {
            if(num[i]!=vis[i])
            {
                int k=m/p[i];
                int count=vis[i]-num[i];//if(vis[i]>num[i])加上后面要被访问的位置之和
                //if(vis[i]<num[i])即被重复访问,减去重复访问对答案的贡献
                sum+=(LL)k*(k-1)/2*p[i]*count;
                for(int j=i;j<tot;j++)//处理访问当前位置对后面位置的影响
                {
                    if(p[j]%p[i]==0)
                        num[j]+=count;//记录每个因子被访问的次数
                }
            }
        }
        printf("Case #%d: %I64d\n",++T,sum);
        //cout<<sum<<"---"<<endl;
    }
}

Frogs

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


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

猜你喜欢

转载自blog.csdn.net/Jane_JXR/article/details/78210839