洛谷P2671 NOIP2015普及组 T3 求和

本文介绍了一种优化组合问题的高效算法,通过统计编号和、数量值之和与编号与数量值乘积之和,实现时间复杂度从O(n^2)降低到O(n),解决了大规模数据集下的组合问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写图片描述

通过暴力枚举每一个三元组时间复杂度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 <cstdio>
#include <iostream>
#include <vector>
#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;t<top1[i];t++){
            int 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;t<top2[i];t++){
            long 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<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值