出题的诀窍

题目链接:https://ac.nowcoder.com/acm/contest/393/C

链接:https://ac.nowcoder.com/acm/contest/393/C
来源:牛客网

题目描述

给定m个长为n的序列a1,a2,,ama1,a2,…,am。

小Z想问你:

ni1=1ni2=1nim=1SUM(a1,i1,a2,i2,,am,im)  mod1000000007∑i1=1n∑i2=1n…∑im=1nSUM(a1,i1,a2,i2,…,am,im)  mod1000000007

其中SUM()SUM(一个序列)表示这个序列中所有不同的数的和,相当于先sort,uniquesort,unique再求和。

输入描述:

第一行两个整数n,m。

接下来m行,每行n个整数,第i行第j个表示ai,jai,j

输出描述:

一行一个整数,表示答案。
示例1

输入

复制
2 3
1 2
2 3
1 3

输出

复制
36

说明

一共有8种情况:

SUM(1,2,1)=3SUM(1,2,3)=6SUM(1,3,1)=4SUM(1,3,3)=4SUM(1,2,1)=3SUM(1,2,3)=6SUM(1,3,1)=4SUM(1,3,3)=4

SUM(2,2,1)=3SUM(2,2,3)=5SUM(2,3,1)=6SUM(2,3,3)=5SUM(2,2,1)=3SUM(2,2,3)=5SUM(2,3,1)=6SUM(2,3,3)=5

把所有数字结果加起来就是36。

备注:

对于所有100%100%的数据,有1n,m2000,0ai,j109

思路:这题打比赛的时候的确不会,范围太大了,看一眼就把深搜啊什么的否定了 。 2000^2000次方,计算机跑爆了都跑不出来,嗯。。。然后果断看下一题,但是又超时了。。。 是真的菜。
嗯。。。 说思路: 这题仔细想想,可以想到其实就是算每个数出现了多少次,我在这里把它称为贡献度吧,所以就是每个数的贡献度之和,就是最后的答案了。
那么具体是怎样的呢? 那么我们现在要解决的难题就是怎么快速得到一个数的贡献度。
我们不妨这样想一下,如果题目没有要求去掉相同的数结果会是什么呢?? 显然就是每一个数乘以 N^(M-1) 次方 然后累加就是答案了。
那么再进一步,去掉相同的数到底是去掉了多少次呢?
对于第i行的数x,若它在前i-1行的数目分别为a1,a2......ai-1
(n-a1)*(n-a2)*.....*(n-a(i-1))保证了前面不会出现相同的,n^(m-i)后面则不需要顾忌,因为就算选到了相同的,也只会把前面的数算进去
累加所有情况就好了
看懂了思路读者不妨先自己试着写一下,也许自己能写出来
下面是AC代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e7+50;
const int mod=1000000007;
typedef long long LL;
struct Node
{
    LL v,w;//存储对应的权值和所在的行
    bool  operator < (const Node x) const
    {
        return v==x.v?w<x.w:v<x.v;
    }


    public: Node(LL aa = 0,LL bb = 0)
    {
        v=aa;
        w=bb;
    }
};
Node a[maxn];
LL sum[maxn];
void Read(LL &n)
{
    n=0;
    LL f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(f=='-') f=-f;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        n=(n<<1)+(n<<3)+(ch^48);
        ch=getchar();
    }
    n=n*f;
}
int main()
{
    LL N,M;
    Read(N);
    Read(M);
//    cout<<N<<" "<<M<<endl;
    int cnt=0;
    for(int i=0;i<M;i++)
    {
        for(int j=0;j<N;j++)
        {
            LL x;
            Read(x);
            a[cnt++]=Node{x,i};
        }
    }
    sort(a,a+cnt);
    sum[0]=1;
    for(int i=1;i<M;i++)//存N^i
    {
        sum[i]=sum[i-1]*N%mod;
    }
    int first=0;
    LL ans=0;
    for(int i=0;i<cnt;i++)//算每个数的贡献度
    {
        int x=1;//x存储第几行
        if(a[i].v!=a[i+1].v)//保证区间里的都是相等的
        {
            LL sum1=0;//存储有多少个
            LL sum2=1;//存储前面几行的数乘起来等于多少
            for(int j=first;j<i;j++)//采用记忆化的思想
            {
                if(a[j].w==a[j+1].w) //找到一行里面有多少个与当前值相等的数
                {
                    sum1++;
                }
                else
                {
                    sum1++;
                    ans=(ans+(((sum2%mod)*sum1%mod)*sum[M-x]%mod)*a[i].v%mod)%mod;
                    sum2=(sum2%mod)*(N-sum1)%mod;
                    x++;
                    sum1=0;

                }
            }
            if(a[i].w==a[i-1].w)
            {
                sum1++;
                ans=(ans+(((sum2%mod)*sum1%mod)*sum[M-x]%mod)*a[i].v%mod)%mod;
            }
            else
            {
                ans=(ans+((sum2%mod)*a[i].v%mod)*sum[M-x]%mod)%mod;
            }
            first=i+1;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

 

 

posted @ 2019-03-21 21:46  执||念  阅读(241)  评论(0编辑  收藏  举报