HDU 6053 TrickGCD+6055 Regular polygon【2017多校联赛2】

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

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

题意:给定a序列,求b序列的方案数,使其满足1、1≤Bi≤Ai

                                                                          2、( l , r ) (1≤l≤r≤n) , gcd(bl,bl+1...br)≥2

思想:类似于素数筛法,
dp[i]:b[]中存在gcd为i的方案数
num[i]:a[]中小于等于i的数有多少个


枚举gcd,对于每个gcd,在a[i]这个位置上的贡献为a[i]/gcd,只要把每个位置上的方案数求出来累乘就好了,但是这个过程还需要优化。优化可以用素数筛法类似的方式,把a[i]相同的数用数组记录个数,那么贡献为k的数有n个,则方案数为k^n,注意去重处理。
时间复杂度就是n*logn*logn。


去重:dp[i]表示gcd为i时的所有方案数,它包含了i的倍数对应的情况,我们需要把这些数减去,但是倍数的情况也有重复,小的倍数会影响大的倍数,比较复杂。因此,我们可以采用逆向思维,从大到小去容斥,这样每次对于dp[i]来说,它的倍数的情况都是已经处理好了的,只要依次减去倍数就可以了,最后对所有的结果求和即可。

参见大神博客:http://blog.csdn.net/jeremy1149/article/details/76221990

CODE:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<math.h>
#include<map>
#include<time.h>
#include<iostream>
#define INF 0x3f3f3f3f
typedef  long long LL;
using namespace std;
const int maxn=100005;
const int mod=1e9+7;
int a[maxn];
LL num[maxn],dp[maxn];
LL qmod(LL x,LL y)
{
    LL w=1;
    while(y)
    {
        if(y&1)
            w=(w*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return w;
}
int main()
{
    int t,T=0,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        int maxx=0;
        memset(num,0,sizeof(num));
        memset(dp,0,sizeof(dp));
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            maxx=max(maxx,a[i]);
            num[a[i]]++;
        }
        for(int i=1; i<=maxx; i++)
            num[i]+=num[i-1];
        for(int i=2; i<=maxx; i++)
        {
            LL res=1;
            if(num[i-1])//相邻的两个数x、x+1的最大公约数为1,即b[]中不存在满足条件的方案数
            {
                dp[i]=0;
                continue;
            }
            for(int j=i; j<=maxx; j+=i)
            {
                LL kk=num[min(j+i-1,maxx)]-num[j-1];
                LL cnt=j/i;
                res=(res*qmod(cnt,kk))%mod;
            }
            dp[i]=res;
        }
        LL ans=0;
        for(int i=maxx; i>=2; i--) //去重
        {
            for(int j=i+i; j<=maxx; j+=i)
                dp[i]=(dp[i]-dp[j]+mod)%mod;
            ans=(ans+dp[i])%mod;
        }
        printf("Case #%d: %lld\n",++T,ans );
    }
}


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

题意:给定n个点,求正多边形的个数。

分析:顶点为整数点的正多边形只有正方形,所以求正方形个数即可。

500个点,暴力枚举四个点看能不能组成正方形肯定不行,我们可以枚举其中两个点(相当于对角线),看剩下两个点存不存在。

由于精度影响,可能会出现小数,可以用二维map标记点是否存在。

已知两点(x1,y1)、(x2,y2),则另外两点为

   [((x1+x2)-(y2-y1))/2,((x2-x1)+(y1+y2))/2]、[((x1+x2)+(y2-y1))/2,((x2-x1)-(y1+y2))/2]

CODE:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<math.h>
#include<map>
#include<iostream>
#define INF 0x3f3f3f3f
typedef  long long LL;
using namespace std;
const int maxn=300005;
const int mod=1e9+7;
map<double ,map<double,int> >vis;
struct point
{
    double  x,y;
} c[505];
int judge(point A,point B)
{
    double x1=A.x+B.x;
    double y1=B.y-A.y;
    double x2=B.x-A.x;
    double y2=A.y+B.y;
    if(vis[(x1-y1)/2.0][(x2+y2)/2.0]&&vis[(x1+y1)/2.0][(y2-x2)/2.0])
        return 1;
    return 0;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int k=0;
        vis.clear();
        for(int i=0; i<n; i++)
        {
            scanf("%lf%lf",&c[i].x,&c[i].y);
            c[i].x+=100;
            c[i].y+=100;
            vis[c[i].x][c[i].y]=1;
        }
        for(int i=0; i<n; i++)
        {
            for(int j=i+1; j<n; j++)
            {
                if(judge(c[i],c[j]))//两条对角线,重复枚举了一遍,
                    k++;
            }
        }
        printf("%d\n",k/2);
    }
}



猜你喜欢

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