【NOIP校内模拟】聚会 party

【描述】
在成都的一条街道上,一共有 N 户人家,每个家庭有 Xi 个人,他们和谐的生活在 一起,作为全国和谐街道,他们经常会小范围组织活动,每次活动会选择一户作为聚点, 并要求某些家庭参加,为了方便通知,村长每次邀请位置连续的家庭。因为每户人数不 同,每个家庭之间有一定距离,村长希望你计算出每次邀请的家庭的移动代价。第 i 个家 庭移动到家庭 j的代价是: Xi*dis(i,j) dis(i,j)表示 i到 j的距离,村长一共安排了 m次聚会,每次邀请[Li,Ri]的家庭参加
【输入】
第一行两个数表示 n,m 第二行 n-1 个数,第 i个数表示第 i 个家庭与第 i+1 个家庭的距离 Di

第三行 n 个数,表示每个家庭的人数 Xi

之后 m行每行三个数 x l r,表示查询要把区间 [l,r]的家庭移动到 x 点的代价和
【输出】
对于每个询问输出一个数表示答案,对 19260817取模
【输入样例】 5 5 2 3 4 5 1 2 3 4 5 1 1 5 3 1 5 2 3 3 3 3 3 1 5 5 【输出样例】 5 5 2 3 4 5 1 2 3 4 5 1 1 5 3 1 5 2 3 3 3 3 3 1 5 5

【子任务】 对于 30%的数据, n,m≤1000 对于另外 20%的数据,所有家庭间的距离都为 1 对于另外 20%的数据,所有家庭人数都为 1 对于 100%的数据 , n,m≤200000;Xi,Di <=2*10^9

我认为这道题是容易想到和前缀和有关的 关键就是在于如何推导式子

我们先假设[l,r]在x的右边,设dis[i]表示i到1的距离,num[i]表示i的人数

ans=(dis[l]-dis[x])num[l]+(dis[l+1]-dis[x])num[l+1].....

合并同类项 其实就是sigma(dis[i]num[i])-dis[x]sigma(num[i])

这样的话只需分别维护dis[i]*num[i]和num[i]的前缀和就可以O(1)回答了

再说[l,r]在x的左边,其实就是和上面的式子刚好反过来

如果[l,r]包含x,分开处理即可

另外这道题减法的时候取模千万要小心。。。。。

#include<bits/stdc++.h>
#define N 200005
#define ll long long
#define submod(a,b) ((((a)-(b))%mod+mod)%mod)
#define endl '\n'
using namespace std;
const ll mod=19260817;
ll n,m;
ll dis[N];//i到1的距离
ll num[N];//i到1的人数和 
ll mul[N];//距离乘人数 

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL),cout.tie(NULL);
    cin>>n>>m;
    ll x;
    for(ll i=2;i<=n;i++)
    {
        cin>>x;
        dis[i]=(dis[i-1]+x)%mod;
    }
    for(ll i=1;i<=n;i++)
    {
        cin>>x;
        num[i]=(num[i-1]+x)%mod;
        mul[i]=(mul[i-1]+dis[i]*x%mod)%mod;
    }
    ll aa,bb,ans;
    while (m--)
    {
        ll x,l,r;
        cin>>x>>l>>r;
        if(r<=x)//[l,r]在x的左边 
        {
//          cout<<submod(dis[x]*submod(num[r],num[l-1])%mod,submod(mul[r],mul[l-1]))%mod<<'\n';
            aa=dis[x]*submod(num[r],num[l-1])%mod;
            bb=submod(mul[r],mul[l-1]);
            cout<<submod(aa,bb)<<endl;
        }
        else if(l>=x)//[l,r]在x的右边 
        {
//          cout<<submod(submod(mul[r],mul[l-1]),dis[x]*submod(num[r],num[l-1])%mod)%mod<<'\n';
            aa=dis[x]*submod(num[r],num[l-1])%mod;
            bb=submod(mul[r],mul[l-1]);
            cout<<submod(bb,aa)<<endl;
        }
        else    //[l,r]包含x
        {
//          cout<<(submod(dis[x]*submod(num[x],num[l-1])%mod,submod(mul[x],mul[l-1]))+submod(submod(mul[r],mul[x]),dis[x]*submod(num[r],num[x])%mod))%mod<<'\n';
            aa=dis[x]*submod(num[x-1],num[l-1])%mod;
            bb=submod(mul[x-1],mul[l-1]);
            ans=submod(aa,bb);
            aa=dis[x]*submod(num[r],num[x])%mod;
            bb=submod(mul[r],mul[x]);
            cout<<(ans+submod(bb,aa))%mod<<endl;
        }
    }
}

转载于:https://www.cnblogs.com/Patrickpwq/articles/9737493.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值