bzoj 3198: [Sdoi2013]spring 题解

来源:互联网 发布:魔剑之刃 源码 编辑:程序博客网 时间:2024/06/05 03:29

【原题】

3198: [Sdoi2013]spring

Time Limit: 40 Sec  Memory Limit: 256 MB
Submit: 253  Solved: 95

Description

Input

Output

Sample Input

3 3
1 2 3 4 5 6
1 2 3 0 0 0
0 0 0 4 5 6

Sample Output

2

HINT



【题解】这道题明明是水题,坑了我两天!!!真是伤心。发现哈希都不熟练了。

首先很容易想到是2^6枚举01状态,使得1的个数不小于K。比如枚举了0110000,我们就可以用一个哈希来处理个数。然后统计一下结果即可。开始的代码如下:

【代码1】

#include<cstdio>#include<cstring>#include<algorithm>#define N 100005using namespace std;int n,k,num,i,j,p,x[N][6],status,Time,flag;typedef long long ll;ll ans,h1,h2;const ll hash1=999997;const ll hash2=233333;int f1[hash1],f2[hash2];int main(){  scanf("%d%d",&n,&k);num=1<<6;  for (i=1;i<=n;i++)    for (j=0;j<6;j++)      scanf("%d",&x[i][j]);  for (status=0;status<num;status++)  {    flag=0;    for (j=0;j<6;j++)      flag+=(status&(1<<j))?1:0;    if (flag<k) continue;    memset(f1,0,sizeof(f1));    memset(f2,0,sizeof(f2));    for (i=1;i<=n;i++)    {      h1=1;h2=1;      for (j=0;j<6;j++)        if (status&(1<<j))        {          h1=(h1+ll(1<<j)*ll(x[i][j]))%hash1;          h2=(h2+ll(1<<j)*ll(x[i][j]))%hash2;        }      Time=min(f1[h1],f2[h2]);      ans+=(long long)Time;      f1[h1]++;f2[h2]++;     }  }  printf("%lld",ans);  return 0;} 

【GO ON】很快就WA了。然后我发现了一个问题:要流量刚好K次。是刚好。也就是说,我多枚举了很多状态。那怎么办?可以用容斥搞一下,加上等于K的,减去等于K+1的,类推。。。以下是代码:

【代码2】

#include<cstdio>#include<cstring>#include<algorithm>#define N 100005using namespace std;int n,k,num,i,j,p,x[N][6],C[7][7],status,Time,flag,del;typedef long long ll;ll ans,h1,h2;const ll hash1=999997;const ll base1=97;const ll hash2=233333;const ll base2=23;int f1[hash1],f2[hash2];int main(){  scanf("%d%d",&n,&k);  for (i=1;i<=n;i++)    for (j=0;j<6;j++)      scanf("%d",&x[i][j]);  C[0][0]=1;    for (i=1;i<=6;i++)    {      C[i][0]=1;      for (j=1;j<=i;j++) C[i][j]=C[i-1][j]+C[i-1][j-1];    }    for (status=0;status<64;status++)  {    flag=0;    for (j=0;j<6;j++)      flag+=(status&(1<<j))?1:0;    if (flag<k) continue;    memset(f1,0,sizeof(f1));    memset(f2,0,sizeof(f2));    Time=0;    for (i=1;i<=n;i++)    {      h1=0ll;h2=0ll;      for (j=0;j<6;j++)        if (status&(1<<j))        {          h1=(h1*base1+ll(x[i][j]))%hash1;          h2=(h2*base2+ll(x[i][j]))%hash2;        }      Time+=min(f1[h1],f2[h2]);      f1[h1]++;f2[h2]++;    }    del=((flag-k)&1)?-1:1;    ans+=(long long)Time*del*C[flag][k];  }  printf("%lld",ans);  return 0;} 

【分析】仔细一想,没什么不对的地方啊。唯一不满的是,我的双关键字哈希可能会被卡!!出于好奇,我就把它挂链了。总算A掉了。话说这个数据是什么心态。。。还有,我自己YY了一个哈希挂链的方法,就是类似于边表的形式(因为以前不怎么写哈希,求神犇轻喷)。

【AC代码】

#include<cstdio>#include<cstring>#include<algorithm>#define N 100005using namespace std;int n,k,num,i,j,p,x[N][6],C[7][7],status,flag,pre,cnt,t;typedef long long ll;ll ans,h,Time,del;const ll hash=99997;const ll base=97;struct arr{int num,s,next;}a[100005];int end[hash],temp[7],f[100005][7],data[100005];ll doit(int k){  int j;  for (int i=end[k];i;i=a[i].next)  {    j=0;    if (a[i].num==t)    for (j=1;j<=t;j++)      if (temp[j]!=f[i][j]) break;    if (j==t+1) return ++a[i].s;  }  if (!end[k]) data[++pre]=k;  cnt++;a[cnt].num=t;a[cnt].s=0;a[cnt].next=end[k];end[k]=cnt;  for (j=1;j<=t;j++) f[cnt][j]=temp[j];return 0;}inline int Read(){  int x=0;char ch=getchar();bool positive=1;  for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') positive=0;  for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';  return positive?x:-x;}int main(){  scanf("%d%d",&n,&k);  for (i=1;i<=n;i++)    for (j=0;j<6;j++)      x[i][j]=Read();  C[0][0]=1;    for (i=1;i<=6;i++)    {      C[i][0]=1;      for (j=1;j<=i;j++) C[i][j]=C[i-1][j]+C[i-1][j-1];    }    for (status=0;status<64;status++)  {    flag=0;pre=0;    for (j=0;j<6;j++)      flag+=(status&(1<<j))?1:0;    if (flag<k) continue;    Time=0ll;    for (i=1;i<=n;i++)    {      h=0ll;t=0;      for (j=0;j<6;j++)        if (status&(1<<j))          h=(h*base+ll(x[i][j]))%hash,temp[++t]=x[i][j];      Time+=doit(h);    }    cnt=0;for (i=1;i<=pre;i++) end[data[i]]=0;    del=((flag-k)&1)?-1ll:1ll;    ans+=(ll)(Time*del*C[flag][k]);  }  printf("%lld",ans);  return 0;}
1 1