洛谷P2671 NOIP2015普及组 T3 求和

洛谷P2671 NOIP2015普及组 T3 求和_第1张图片

通过暴力枚举每一个三元组时间复杂度 O(n2) ,期望得分60.
n<=100000 ,要考虑怎么把时间复杂度降下来.
num[x] N(x) .三元组的分数计算公式为 (x+z)(N(x)+N(z))
可以发现,一个三元组的得分与中间项无关,另两项应满足颜色相同且同奇偶(即满足 (x+z)mod2==0 ),所以把同颜色,同奇偶的项放到一起,这样在循环计算时就不必要判断得分的合法性.
可是这样做还是不能减少循环,计算过程中,一个纸片的得分被计算了多次,能不能把前面算过的合并成一个呢?
对于两个同色同奇偶的纸片x,y, y与x的得分为
(x+y)(N(x)+N(y))=xN(x)+yN(y)+xN(y)+yN(x)
对于三个同色同奇偶的纸片x,y,z, z与x,y的得分为
(x+z)(N(x)+N(z))+(y+z)(N(y)+N(z))
=xN(x)+yN(y)+2zN(z)+(x+y)N(z)+(N(x)+N(y))z
可以发现,对于已经计算完的纸片,可以统计编号和、num值之和 与 编号与num值乘积之和,每次计算将他们加到答案中,可以省掉一层循环.时间复杂度降为 O(n)

#include 
#include 
#include 
#define N 100005
using namespace std;
const int mod=10007;
long long num[N],top1[N],top2[N],d[N];
vector<int> a[N];
vector<int> b[N];
long long ans;
int main(){
    //freopen("sum.in","r",stdin); freopen("sum.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&num[i]),d[i]=i*num[i];
    for(int i=1;i<=n;i++) {
        int x;
        scanf("%d",&x);
        if(i&1) a[x].push_back(i),top1[x]++;
        else b[x].push_back(i),top2[x]++;
    }
    for(int i=1;i<=m;i++){
        long long x,y,z;
        if(top1[i]>0) x=d[a[i][0]],y=a[i][0],z=num[y];
        for(int t=1;tint j=a[i][t];
            ans=(ans+x+(long long)y*num[j]+z*j+t*d[j])%mod;
            x=(x+d[j])%mod; y=(y+j)%mod; z=(z+num[j])%mod;
        }
        if(top2[i]>0) x=d[b[i][0]],y=b[i][0],z=num[y];
        for(int t=1;tlong long j=b[i][t];
            ans=(ans+x+(long long)y*num[j]+z*j+t*d[j])%mod;
            x=(x+d[j])%mod; y=(y+j)%mod; z=(z+num[j])%mod;
        }
    }
    cout<return 0;
}

你可能感兴趣的:(递推)