sdoi2013 spring(hash+容斥)

大体思路是先求出来(f[i])代表有至少(i)个位置相同的点对数。
然后就已经没什么好害怕的了(跟BZOJ3622一样)
然后这个(f[i)]怎么求呢?
最无脑的方法就是枚举位置,然后(hash)表记一下每种情况出现多少次然后把(sum_{情况个数}{情况次数*(情况次数-1)})加到(f[)枚举的位置个数(])。就行了。
发现这个方法复杂度足以通过此题。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int N=101000;
const int mod=23333;
const int p=19260817;
int cnt,head[25000];
struct edge{
	int nxt,id,c;
}e[N];
int a[N][10],n,C[10][10],num,b[10],f[10],ans,mmp;
bool judge(int now,int x,int y){
	for(int i=1;i<=num;i++)
		if(a[x][b[i]]!=a[y][b[i]])return false;
	return true;
}
void add(int u,int id){
	cnt++;
	e[cnt].nxt=head[u];
	e[cnt].id=id;
	e[cnt].c=1;
	head[u]=cnt;
}
int ins(int now,int id,int x){
	for(int i=head[x];i;i=e[i].nxt)
		if(judge(now,e[i].id,id)){
			e[i].c++;
			return e[i].c-1;
		}
	add(x,id);
	return 0;
}
int read(){
	int sum=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return sum*f;
}
signed main(){
	n=read();mmp=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=6;j++)
			a[i][j]=read();
    C[0][0]=1;
    for(int i=1;i<=6;i++) {
        C[i][0]=1;
        for(int j=1;j<=i;j++)C[i][j]=C[i-1][j]+C[i-1][j-1];
    }
	for(int i=0;i<(1<<6);i++){
		num=0;
		memset(head,0,sizeof(head));cnt=0;
		for(int j=1;j<=6;j++)if(i&(1<<(j-1)))b[++num]=j;
		for(int j=1;j<=n;j++){
			int w=0;
			for(int k=1;k<=num;k++)
				w=(w*p%mod+a[j][b[k]]%mod)%mod;
			f[num]+=ins(i,j,w);
		}
	}
	int type=1;
	for(int i=mmp;i<=6;i++){
		ans+=type*C[i][mmp]*f[i];
		type=-type;
	}
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Xu-daxia/p/10479850.html