题目链接:https://ac.nowcoder.com/acm/contest/393/C
这个题就是对于每个数算贡献,如果有相同的数,只计算先出现的数的贡献
对于数x,若它在前i行的数目分别为a1,a2......ai。则这个数的贡献应为:ai*x*(n-a1)*(n-a2)*.....*(n-a(i-1))*n^(m-i)次方。这样想,如果不要求不重复算,那大小就直接是ai*n^(m-1)了
(n-a1)*(n-a2)*.....*(n-a(i-1))保证了前面不会出现相同的,n^(m-i)后面则不需要顾忌,因为就算选到了相同的,也只会把前面的数算进去
这个题非常玄学,不用快读时而过,时而不过,用了快读就稳过,以后能用快读就还是尽量用吧。
代码实现:首先将所有数字从小到大排序,第二关键字为所在的行,这样一来,相同的数字排到了一起,并且相同数字且在同一行的数也排在了一起,这样就能很方便地统计每种数字,在每一行出现的次数
用前后是否相同来判断是否到了分界点,具体实现见代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=1<<30; const int maxn=2001; const double pi=acos(-1); const int mod=1000000007; struct node{ int a,b; bool operator < (const node z) const{ return a==z.a?b<z.b:a<z.a; } }p[maxn*maxn]; ll q[maxn]; inline void read(int &x){ char ch=x=0; while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); } int main(){ int n,m;read(n);read(m); q[0]=1;for(int i=1;i<=m;i++) q[i]=(q[i-1]*n)%mod; ll ans=0,cnt=0; for(int i=1;i<=m;i++) for(int j=0;j<n;j++){ int x;read(x); p[++cnt]=node{x,i}; } sort(p+1,p+cnt+1);int first=1; for(int i=1;i<=cnt;i++){ int t=1;//t存储的是在一行里某个数x出现的次数 if(p[i].a!=p[i+1].a){ int k=0;ll s=1;//k存储的是行数,s存储的是(n-a1)*(n-a2)......那一串 for(int j=first;j<i;j++){//之所以j=i的时候要单独拿下去加,就是因为你比较的必须保证是同一行的 if(p[j].b==p[j+1].b)t++;else{ k++; ans=(ans+(ll)p[j].a*t%mod*s%mod*q[m-k])%mod; //cout<<233<<endl; s=(s%mod*(n-t))%mod; t=1; } } ans=(ans+(ll)p[i].a*t%mod*s%mod*q[m-k-1])%mod;//这个加和循环里的加其实没区别,因为少了k++这一步q数组多减一个1 first=i+1; } } cout<<ans<<endl; return 0; }