牛客练习赛42 C 出题的诀窍

题目链接: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;
}
原文地址:https://www.cnblogs.com/qingjiuling/p/10553342.html