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 );
}
}
题意:给定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);
}
}