HDU 5080 Colorful Toy(polya+计算几何)

85 篇文章 0 订阅
13 篇文章 0 订阅

Description
给出n个整点的坐标以及这n个点之间连的m条边,现用c种颜色给这n个点染色,旋转后重合视为同一种方案,问一共有多少种不同的方案
Input
第一行为一整数表示用例组数T,每组用例第一行为三个整数n,m,c分别表示点数,边数以及颜色数,之后n行每行两个整数表示该点坐标,最后m行每行两个整数a,b表示点a和点b之间有一条边
(1<=T<=20,1<=n<=50,0<=m<=n*(n-1)/2,1<=c<=100)
Output
对于每组用例,输出染色方案数,结果模1e9+7
Sample Input
2
5 6 2
0 0
1 0
0 1
-1 0
0 -1
1 2
1 3
2 3
3 4
4 5
5 2
5 4 2
0 0
1 0
0 1
-1 0
0 -1
2 3
3 4
4 5
5 2
Sample Output
32
12
Solution
polya,首先暴力搞出置换的个数,由于是整点所以只有以图形中心旋转0度,90度,180度以及270度才可能为一种置换(为方便坐标转换可以将所有点的坐标都减去中心点的坐标使得中心点变成原点),暴力枚举这三种旋转(不转显然是一种置换),对于每种旋转求出旋转后的点的坐标,然后将这些点与原来的点一一对应(点相同),然后将原来点的关系矩阵映到新的点中再判断这两个关系矩阵是否相同(边相同),如果都相同则为一个置换,再根据原先点和旋转后点的映射关系求出轮换数,之后套polya定理即可
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define maxn 55
#define mod 1000000007
#define eps 1e-4
typedef long long ll;
struct node
{
    double x,y;
}p[maxn],tp[maxn];
int T,n,m,c,M[maxn][maxn],tM[maxn][maxn],id[maxn],cnt;
double x,y;
ll ans;
ll mod_pow(ll a,ll b,ll p)
{
    ll ans=1ll;
    a%=p;
    while(b)
    {
        if(b&1) ans=(ans*a)%p;
        a=(a*a)%p;
        b>>=1;
    } 
    return ans;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&c);
        for(int i=0;i<maxn;i++)
            for(int j=0;j<maxn;j++)M[i][j]=0;
        x=y=0;
        cnt=ans=1;
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&p[i].x,&p[i].y);
            x+=p[i].x,y+=p[i].y;
        }
        x/=n,y/=n;
        for(int i=0;i<n;i++)p[i].x-=x,p[i].y-=y;//将图平移使得中心点是原点 
        while(m--)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            a--,b--;
            M[a][b]=M[b][a]=1;
        }
        for(int i=0;i<n;i++)ans=ans*c%mod;//不旋转 
        for(int k=1;k<4;k++)//旋转k*90度 
        {
            int flag=1;
            memset(id,0,sizeof(id));
            for(int i=0;i<maxn;i++)
                for(int j=0;j<maxn;j++)tM[i][j]=0;
            //坐标转换 
            if(k==1)for(int i=0;i<n;i++)tp[i].x=-p[i].y,tp[i].y=p[i].x;
            else if(k==2)for(int i=0;i<n;i++)tp[i].x=-p[i].x,tp[i].y=-p[i].y;
            else if(k==3)for(int i=0;i<n;i++)tp[i].x=p[i].y,tp[i].y=-p[i].x;
            for(int i=0;i<n&&flag;i++)//判断旋转后点的坐标与原先点是否能一一对应 
            {
                int temp=0;
                for(int j=0;j<n&&!temp;j++)
                    if(fabs(p[j].x-tp[i].x)<eps&&fabs(p[j].y-tp[i].y)<eps)
                        temp=1,id[j]=i;
                if(!temp)flag=0;
            }
            for(int i=0;i<n;i++)//求出旋转后点的关系矩阵 
                for(int j=0;j<n;j++)
                    if(M[i][j])tM[id[i]][id[j]]=1;
            for(int i=0;i<n;i++)//判断两个关系矩阵是否相同 
                for(int j=0;j<n;j++)
                    if(M[i][j]!=tM[i][j])flag=0;
            if(flag)//合法置换 
            {
                int res=0;
                for(int i=0;i<n;i++)//求轮换数 
                {
                    if(id[i]==-1)continue;
                    int j=i;
                    while(id[j]!=i&&~j)
                    {
                        int temp=j;
                        j=id[j],id[temp]=-1;
                    }
                    id[j]=-1;
                    res++;
                }
                ll temp=1;
                for(int i=0;i<res;i++)temp=temp*c%mod;
                ans=(ans+temp)%mod;
                cnt++;
            }
        }
        ans=ans*mod_pow(cnt,mod-2,mod)%mod;
        printf("%lld\n",ans);
    }
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值