• NOIP模拟——聚会


    题目

    在成都的一条街道上,一共有 NN户人家,每个家庭有 XiXi 个人,他们和谐的生活在 一起,作为全国和谐街道,他们经常会小范围组织活动,每次活动会选择一户作为聚点, 并要求某些家庭参加,为了方便通知,村长每次邀请位置连续的家庭。因为每户人数不 同,每个家庭之间有一定距离,村长希望你计算出每次邀请的家庭的移动代价。第 ii个家 庭移动到家庭 jj的代价是:Xidis(i,j)Xi∗dis(i,j)

    dis(i,j)dis(i,j)表示ii 到jj的距离,村长一共安排了 mm 次聚会,每次邀请[LiRi][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
    样例输出
    125
    72
    9
    0
    70
    提示

    对于 30%的数据, n,m≤1000

    对于另外 20%的数据,所有家庭间的距离都为 1

    对于另外 20%的数据,所有家庭人数都为 1

    对于 100%的数据 , n,m≤200000;Xi,Di <=2*10^9

      其实只需要维护前缀和就行了,维护一个x[i]*dis[i]和一个x[i];

    我们只需要考虑三种情况:

    目标在区间左边的时候:ans=Σb[i]*x[i]-bΣx[i];

    目标在区间右边的时候:ans=bΣx[i]-Σb[i]*x[i];

    目标在区间内部的时候:分左右两边分别考虑;

    O(n)就可以做出来了

    但是考场上没有看出来,写了个线段树,维护区间所有人到区间两端的花费,这样每次询问logn就可以解了,但是很复杂,代码详细看吧

    O(n)正解

    #include <iostream> 
    #include <cstdio> 
    using  namespace  std; 
    typedef  long  long LL; 
    const  LL MOD = 19260817; 
    const  int MAXN = 200010; 
    int  n, m; 
    LL d[MAXN], a[MAXN]; 
    LL sum_a[MAXN], sum_d[MAXN], sum_mul[MAXN]; 
    LL calc_ans(int   x, int  l, int  r, bool  left) { 
        if(l > r) return  0; 
        LL ans1 = ((sum_a[r] - sum_a[l-1]) % MOD + MOD) % MOD; 
        ans1 = ans1 * sum_d[x] % MOD; 
        LL ans2 = ((sum_mul[r] - sum_mul[l-1]) % MOD + MOD) % MOD; 
        if(!left) swap(ans1, ans2); 
        return  ((ans1 - ans2) % MOD + MOD) % MOD; 
    } 
    int  main() { 
        scanf("%d%d", &n, &m); 
        for (int  i=2; i<=n; i++) { 
            scanf("%lld", &d[i]); 
            sum_d[i] = (sum_d[i-1] + (d[i] %= MOD)) % MOD; 
        } 
        for (int  i=1; i<=n; i++) { 
            scanf("%lld", &a[i]); 
            sum_a[i] = (sum_a[i-1] + (a[i] %= MOD)) % MOD; 
            sum_mul[i] = (sum_mul[i-1] + a[i] * sum_d[i] % MOD) % MOD; 
        } 
        for (int  i=1, x, l, r; i<=m; i++) { 
            scanf("%d%d%d", &x, &l, &r); 
            LL ans1 = calc_ans(x, l, min(r, x-1), true); 
            LL ans2 = calc_ans(x, max(l, x+1), r, false); 
            printf("%lld
    ", (ans1 + ans2) % MOD); 
         } 
        return 0; 
    }

    我自己的代码

    #include<bits/stdc++.h>
    using namespace std;
    #define lc u*2
    #define rc (u*2)+1
    #define mid (l+r)/2
    #define mod 19260817
    #define ll long long
    inline ll read(){
        char ch=getchar();
        ll res=0;
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
        return res;
    }
    ll tot[800050],m,y,d[200050],n[200050],lv[800050],rv[800050],sum[800050];//lv(rv):当前区间所有人走到区间最左(右)边的 
    inline void buildtree(int u,int l,int r)
    {
        if(l==r)
        {
            return;
        }
        buildtree(lc,l,mid);
        buildtree(rc,mid+1,r);
        lv[u]=lv[rc]+lv[lc]+((tot[r]-tot[mid]+mod)%mod)*(sum[mid+1]-sum[l]+mod)%mod;//合并线段树 
        lv[u]%=mod;
        rv[u]=rv[lc]+rv[rc]+((tot[mid]-tot[l-1]+mod)%mod)*(sum[r]-sum[mid]+mod)%mod;
        rv[u]%=mod;
    }
    inline ll findl(int u,int l,int r,int st,int des)//找区间内所有点到最左边的花费 
    {
        if(r<st||l>des) return 0;    
        if(l==r)return lv[u]+(n[l])*((sum[l]-sum[st]+mod)%mod)%mod;
        if(st<=l&&r<=des) return lv[u]+(((tot[r]-tot[l-1]+mod)%mod)*(sum[l]-sum[st]+mod)%mod)%mod;
        return (findl(lc,l,mid,st,des)+findl(rc,mid+1,r,st,des))%mod;
    }
    inline ll findr(int u,int l,int r,int st,int des)//区间所有点到区间最右边的花费 
    {
        if(r<st||l>des) return 0;    
        if(l==r)return (rv[u]+(n[l]+mod)*(sum[des]-sum[r]+mod)%mod+mod)%mod;
        if(st<=l&&r<=des) return (rv[u]+((tot[r]-tot[l-1]+mod)%mod)*(sum[des]-sum[r]+mod)%mod+mod)%mod;
        return (findr(lc,l,mid,st,des)+findr(rc,mid+1,r,st,des))%mod;
    }
    inline ll query(int u,int l,int r,int st,int des,int to)
    {
        if(st>des) return 0;
        if(to<=st)
        {
            return (((sum[st]-sum[to]+mod)%mod)*(tot[des]-tot[st-1]+mod)%mod+findl(1,1,y,st,des))%mod;
        }
        else if(des<=to) 
        {
            return (((sum[to]-sum[des]+mod)%mod)*(tot[des]-tot[st-1]+mod)%mod+findr(1,1,y,st,des))%mod;
        }
        else
        {
            return (query(1,1,y,st,to-1,to)+query(1,1,y,to+1,des,to))%mod;
        }
    }
    int main(){
    //    freopen("party.in","r",stdin);
    //    freopen("party.out","w",stdout);
        y=read(),m=read();
        for(int i=1;i<y;i++)
            d[i]=read(),sum[i+1]=(sum[i]+d[i])%mod;//距离前缀和 
        for(int i=1;i<=y;i++)
            n[i]=read(),tot[i]=(tot[i-1]+n[i])%mod;//人数前缀和 
        buildtree(1,1,y);
        int x=0,q=0,o=0;
        for(int i=1;i<=m;i++)
        {
            x=read(),q=read(),o=read();
            cout<<(query(1,1,y,q,o,x)+mod)%mod<<'
    ';
        }
        return 0;
    }
  • 相关阅读:
    [POJ1724]ROADS
    表达式求值
    [NOIp2017提高组]奶酪(BFS)
    [NOIp2012提高组]Vigenère 密码
    [NOIp2012提高组]国王游戏
    [POJ1321]棋盘问题
    [POJ3009]Curling2.0
    垃圾陷阱
    2019CSP day1t2 括号树
    2019CSP游记
  • 原文地址:https://www.cnblogs.com/forever-/p/9737201.html
Copyright © 2020-2023  润新知