Rivendell’s pearls(pearls) (hash+容斥原理)

Rivendell’s pearls(pearls)
【问题描述】
Rivendell 是一个心灵手巧的男孩子,他在闲暇的时候喜欢做一些小饰品。
有一天 Rivendell用漂亮的珍珠做成了 n串手链,并且每串手链都由 4个珍
珠构成,并且每粒珍珠都有一种颜色,颜色用小写字母和数字表示。现在他突然
想知道这n 串手链中有多少对有且仅有k 粒珍珠是不同颜色的。
【输入格式】
第一行两个整数 n和 k。
接下来 n个长度为 4的字符串。
【输出格式】
一个整数表示答案。
【样例输入】
4 2
0000
a010
0202
a0e2
【样例输出】
3
【数据规模及约定】
对于 15%的数据,n<=2000
对于 60%的数据,k<=2。其中50%的数据,k=1
对于 75%的数据,字符串中只有字母a 到f
对于 100%的数据,n<=50000,k<=4


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ul unsigned long long 
#define LL long long
#define p 2000001001
#define N 50003
using namespace std;
int n,m,k,len;
char s[10];
int ch[N][10],ans[N],vis[N];
LL tot,base[10][10];
ul mi[N],hash[N];
struct data 
{
	int x;
	ul num,num1;
}a[N];
int cmp(data x,data y)
{
	return x.num<y.num||x.num==y.num&&x.num1<y.num1;
}
void solve1()
{
	for (int i=1;i<=n;i++)
	 {
	 	a[i].num=hash[i],a[i].num1=0;
	 	a[i].x=i;
	 	for (int j=1;j<=k;j++)
	 	 a[i].num-=ch[i][ans[j]]*mi[ans[j]-1],
	 	 a[i].num1-=ch[i][ans[j]]*mi[ans[j]-1];
	 }
	sort(a+1,a+n+1,cmp);
	LL sum=1,sum1=1;
	for (int i=2;i<=n;i++)
	 if (a[i].num==a[i-1].num) 
	 {
	  if (a[i].num1==a[i-1].num1)  sum1++;
	  else tot-=(sum1-1)*sum1/2,sum1=1;
	  sum++;
	  if (i==n)  tot-=sum1*(sum1-1)/2,tot+=(sum-1)*sum/2;//cout<<tot<<endl;
     }
	 else {
	    tot-=(sum1-1)*sum1/2;
	 	tot+=(sum-1)*sum/2;
	 	sum=1,sum1=1;
	 }
}
void dfs(int x,int now)
{
	if (x==k+1)
	 {
	 	if (k==1)  solve1();
	 	return; 
	 }
	for (int i=now;i<=4;i++)
	 {
	 	ans[x]=i;
	 	dfs(x+1,i+1);
	 }
}
void solve()
{
	for (int i=1;i<=n;i++)
	 {
	 	a[i].num=hash[i],a[i].num1=0;
	 	a[i].x=i;
	 	for (int j=1;j<=len;j++)
	 	 a[i].num-=ch[i][ans[j]]*mi[ans[j]-1];
	 }
	sort(a+1,a+n+1,cmp);
	LL sum=1,ans1=base[k][len]; 
	for (int i=2;i<=n;i++)
	 if (a[i].num==a[i-1].num) 
	 {
	  sum++;
	  if (i==n)  tot+=ans1*(sum-1)*sum/2;
     }
	 else {
	 	tot+=ans1*(sum-1)*sum/2;
	 	sum=1;
	 }
}
void dfs1(int x,int now)
{
	if (x==len+1)
	 {
	 	solve();
	 	return; 
	 }
	for (int i=now;i<=4;i++)
	 {
	 	ans[x]=i;
	 	dfs1(x+1,i+1);
	 }
}
LL calc()
{
	    sort(hash+1,hash+n+1);
		LL sum=1,ans1=0;
		for (int i=2;i<=n;i++)
		 if (hash[i]==hash[i-1])
		  {
		  	sum++;
		  	if (i==n) ans1+=sum*(sum-1)/2; 
		  }
		  else {
		  	ans1+=sum*(sum-1)/2;
		  	sum=1;
		  }
		return ans1;
}
int main()
{
	freopen("pearls.in","r",stdin);
	freopen("pearls.out","w",stdout);
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		for (int j=1;j<=4;j++)
		 ch[i][j]=s[j];
	}
    mi[0]=1;
	for (int i=1;i<=4;i++) mi[i]=mi[i-1]*p;
	for (int i=1;i<=n;i++)
	 {
	 	for (int j=1;j<=4;j++)
	 	 hash[i]+=mi[j-1]*ch[i][j];
	 }
	if (k==0)
	{
		sort(hash+1,hash+n+1);
		LL sum=1,ans1=0;
		for (int i=2;i<=n;i++)
		 if (hash[i]==hash[i-1])
		  {
		  	sum++;
		  	if (i==n) ans1+=sum*(sum-1)/2; 
		  }
		  else {
		  	ans1+=sum*(sum-1)/2;
		  	sum=1;
		  }
		printf("%I64d\n",ans1);
		return 0;
	}
	if (k==1)
	{
		dfs(1,1);
		printf("%I64d\n",tot);
		return 0;
	}
	base[2][0]=6; base[2][1]=-3; base[2][2]=1;
	base[3][0]=-4; base[3][1]=3; base[3][2]=-2; base[3][3]=1;
	base[4][0]=1; base[4][1]=-1; base[4][2]=1; base[4][3]=-1; base[4][4]=1;
	for (int i=1;i<=k;i++)
	 {
	 	len=i;
	 	dfs1(1,1);
	 }
	printf("%I64d\n",tot+calc()*base[k][0]);
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值