bzoj 3198 [Sdoi2013]spring hash+容斥原理

28 篇文章 0 订阅
12 篇文章 0 订阅

Description


有n条河流,每条河流有6个特征。对于给定的k,求多少对河流有恰好k个特征相等
n ≤ 1 0 5 n\le10^5 n105

Solution


知道了套路就驾轻就熟了。。
我们只需要求出至少k个特征的答案,那么26枚举这些特征然后用hash判一下,最后套一个容斥就可以了
嗯,似乎非常简单,至少看起来是这样的

但是这里的hash似乎需要一些高超技巧,我们简单地当成6位BASE进制膜一个大质数是行不通的。。。
本着发扬OI不是口胡竞赛的愿景,我对着一个标膜了又膜终于只拿到了92分。一波换质数+换底数都没有成功A掉。。直接上map又会T掉,想了想还是不管了。。

综上,这是一份不能AC的92分代码╮(╯▽╰)╭

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef long long LL;
const int MOD=1234567;
const int N=500100;

LL a[N][10],n;
LL c[10][10];

struct Hash {
	int tot,h[MOD+10],v[MOD+10];
	LL rec[MOD+10];
	void clear() {
		rep(i,1,tot) rec[v[i]]=h[v[i]]=0;
		tot=0;
	}
	LL calc() {
		LL res=0;
		rep(i,1,tot) {
			if (rec[v[i]]&1) {
				res+=(rec[v[i]]-1)/2*rec[v[i]];
			} else res+=rec[v[i]]/2*(rec[v[i]]-1);
		}
		return res;
	}
	bool equal(int x,int y,int S) {
		bool flag=true;
		rep(i,0,5) if ((S>>i)&1) flag&=(a[x][i+1]==a[y][i+1]);
		return flag;
	}
	void insert(int tmp,int pos,int S) { tmp%=MOD;
		while (h[tmp]&&!equal(h[tmp],pos,S)) tmp=(tmp+1)%MOD;
		if (!h[tmp]) v[++tot]=tmp,h[tmp]=pos;
		rec[tmp]++;
	}
} Hash;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

LL calc(int S) {
	Hash.clear();
	rep(i,1,n) {
		LL tmp=0;
		rep(j,1,6) {
			tmp=tmp*233%MOD;
			if ((S>>(j-1))&1) tmp=(tmp+a[i][j]+1)%MOD;
		}
		Hash.insert(tmp,i,S);
	}
	return Hash.calc();
}

int main(void) {
	freopen("data.in","r",stdin);
	n=read(); int k=read();
	c[1][0]=c[1][1]=1;
	rep(i,2,7) {
		c[i][0]=1;
		rep(j,1,i) c[i][j]=c[i-1][j]+c[i-1][j-1];
	}
	rep(i,1,n) rep(j,1,6) a[i][j]=read();
	LL ans=0;
	for (int S=0;S<64;++S) {
		int cnt=__builtin_popcount(S);
		if (cnt<k) continue;
		LL xs=((cnt-k)&1)?-1:1;
		ans+=xs*calc(S)*c[cnt][k];
	}
	printf("%lld\n", ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值