EOJ #276

时间:2024-10-23 10:03:56

题面

感觉是个套路题,不是特别难(然而卡常

直接做不可做,改成算每个数的贡献

暴力的想法是容斥,即记录每个数在每行里的出现情况,从总方案中扣掉每一行都没选到这个数的方案,复杂度$O(n^3)$

我们发现很多时候一行里根本没有这个数,也就是说很多情况下都白枚举了,我们可以尝试直接对每个数求方案。具体来说我们把每个数出现的行丢进一个vector里,然后vector里一段相同的就是它在这行出现的次数,没出现的行可以直接算出来,这样复杂度就变成$O(n^2\log^2 n)$了,然后你就可以开始卡常了

用了fread+register+指针+unsigned int+const 才在 EOJ 上卡过去,我佛了

 #pragma GCC optimize(2)
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define uint unsigned int
#define vint vector<uint>
using namespace std;
const int N=;
const uint mod=1e9+;
vint vec[N*N];
uint n,m,rd,all,tot,ans;
uint a[N][N],pw[N],uni[N*N]; char BF[<<],*P1=BF,*P2=BF;
char Gc(){return (P1==P2&&(P2=(P1=BF)+fread(BF,,<<,stdin),P1==P2)?EOF:*P1++);}
void Fread(uint &x)
{
x=; char ch=Gc();
while(!isdigit(ch)) ch=Gc();
while(isdigit(ch)) x=(x<<)+(x<<)+(ch^),ch=Gc();
} void Add(uint &x,uint y)
{
x+=y;
if(x>=mod) x-=mod;
}
int main()
{
register uint i,j,k;
Fread(m),Fread(n);
for(i=;i<=n;i++)
for(j=;j<=m;j++)
Fread(rd),uni[++tot]=*(*(a+i)+j)=rd;
sort(uni+,uni++tot);
const uint lth=unique(uni+,uni++tot)-uni-;
for(i=;i<=n;i++)
for(j=;j<=m;j++)
{
const uint t=lower_bound(uni+,uni++lth,*(*(a+i)+j))-uni;
vec[*(*(a+i)+j)=t].push_back(i);
}
for(i=pw[]=;i<=n;i++) pw[i]=1ll*pw[i-]*m%mod;
for(i=;i<=lth;i++)
{
uint tmp=pw[n],tep=,res=n;
const uint siz=vec[i].size();
vint ve=vec[i];
for(j=;j<siz;j=k+)
{
k=j;
while(k+<siz&&ve[j]==ve[k+]) k++;
tep=1ll*tep*(m-(k-j+))%mod,res--;
}
tep=1ll*tep*pw[res]%mod;
Add(tmp,mod-tep),Add(ans,1ll*tmp*uni[i]%mod);
}
printf("%d",ans);
return ;
}