思路:
这道题目如果不考虑重复就很简单了,就是先求出总和sum,ans = sum*m^(n-1)(这里我n表示行数,m表示列数,
与题中给出的n,m表示的意义不同)。
考虑重复的就是对每一行进行考虑,对每一行的每一个元素x进行计算,
用序列a1,a2,a3……ai……an表示元素x在前i行(1-i行)出现的次数,(求解ai可以用a2 = a1*k,k表示在第i行x出现的次数)
所以每行的不同元素x对整个排序的贡献是(m-a1)*(m-a2)*(m-a3)……(m-ai-1)*ai*x*m^(n-i),
其中m*(n-i)表示后面的元素序列的组合,(这里不用考虑后面重复的元素,因为就算选到了的相同的元素也只算当前的
元素个数,后面的不算,以样例中的的3为例,只有
(1,3,1),(1,3,3),(2,3,1),(2,2,3),四种情况,其中我们统计第二行3时会统计(1,3,1),(1,3,3),(2,3,1),(2,2,3)四种情况,所以3出现4次,而且后面的3也没算,因为这里统计的只是当前的3,再统计第三行的3时会统计到
(1,2,3),(2,2,3)两种情况。
)
具体细节参见代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 4000400;
const LL MOD = 1e9+7;
LL dd[maxn]={0};
struct Node{
LL id,data;
Node(LL x,LL y):data(x),id(y){}
Node(){}
}cur[maxn];
bool cmp(Node a,Node b){
if(a.data!=b.data) return a.data<b.data;
else return a.id<b.id;
}
int main(void)
{
LL m,n,i,j,cnt=0,x;
scanf("%lld%lld",&m,&n);
for(dd[0]=1,i=1;i<=n;i++) dd[i]=dd[i-1]*m%MOD; //预处理m^i
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
scanf("%lld",&x);
cur[++cnt] = Node(x,i); //存入节点
}
}
sort(cur+1,cur+cnt+1,cmp);
LL pos=1,t1,t2,ans=0,tp;
for(i=1;i<=cnt;i++)
if(cur[i].data!=cur[i+1].data){ //统计同一个数在不同层出现的次数
t1=1,t2=0,tp=1;
for(j=pos;j<=i;j++){
if(cur[j].id==cur[j+1].id) t1++; //t1表示它在同一层出现的次数
else{
t2++; //t2表示它当前在第几层
ans = (ans+(cur[j].data%MOD*t1%MOD*tp%MOD*dd[n-t2]%MOD)%MOD)%MOD;//统计一个数字在第t2层出现的次数对所有出现次数做出的贡献。
tp = (tp%MOD*(m-t1))%MOD;//tp表示在前i层(就是1~i-1层中与ai不同的数字的组合方式),就是(n-a1)*(n-a2)……(n-ai)的值。
t1 = 1;
}
}
//单独处理最后一个,因为最后一个的层数和前面的层数没有关系。
ans = (ans+(cur[i].data%MOD*t1%MOD*tp%MOD*dd[n-t2-1]%MOD)%MOD)%MOD;
pos = i+1;
}
printf("%lld\n",ans);
return 0;
}