G牛牛的Link Power II(加等数列+线段树)

题意:有长度为n的01串,设s[i]=1,s[j]=1,让你计算所有满足s[i]=1,s[j]=1且1<=i<j<=n的j-i的总和,下面有m次操作+询问,问操作前和每一次操作的答案,1<=n,m<=1e5

感想:这比赛是叫寒假**基础 **算法训练营,玩了2个小时的树状数组一直TLE,后来想想算错法时间复杂度了,看了看题解,加等数列是个什么鬼,还没怎么学,先转转大佬链接,嗯!很基础 !然后在这个加等数列的思想上套个线段树,开两个线段树维护一下[1,pos-1]和[pos+1,n]的值就行了,每加/减一个1,每棵线段树对应的区间就加/减1,每次询问的答案就是tree1[1,pos-1]+tree2[pos,n],主要还是再要一个线段树的模板
然后的话加等数列还行,反正我只会模板。。。。。如果是加等多项式的话,玩了几个小时终于放弃了,先给个加等数列的模板吧
至于为什么这么做是对的自己列两个表就知道了,如果要构造多项式,靠的还是脑子,干。

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define ld long double
#define rep1(i,n) for(int i=1;i<=n;i++)
#define rep0(i,n) for(int i=0;i<n;i++)
#define rep(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
const ll mod=1e9+7;
const int maxn=1e5+7;
int n,m,d2[maxn],l,r,a,k;
void add(int l,int r,int a,int k)
{
    d2[l]+=a;
    d2[l+1]+=k-a;
    d2[r+1]-=(r-l+1)*k+a;
    d2[r+2]-=(l-r)*k-a;
}
void pre_sum()
{
    for(int i=1;i<=n;++i)
    {
        d2[i]+=d2[i-1];
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d %d %d %d",&l,&r,&a,&k);
        add(l,r,a,k);
    }
    pre_sum();
    pre_sum();
    for(int i=1;i<=n;++i)
    {
        printf("%d%c",d2[i],i==n?'\n':' ');
    }
    return 0;
}

这里就是答案

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define ld long double
#define rep1(i,n) for(int i=1;i<=n;i++)
#define rep0(i,n) for(int i=0;i<n;i++)
#define rep(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
const ll mod=1e9+7;
const int maxn=1e5+7;
char s[maxn];
int n,m,x,q;
ll ans;
struct node
{
    ll sum,lazy;
    int l,r;
};

struct Segement_tree
{
    node t[4*maxn];
    void pushdown(int root)
    {
        if(t[root].lazy!=0)
        {
            t[root].sum+=t[root].lazy*(t[root].r-t[root].l+1);//线段树更新值
            if(t[root].l!=t[root].r)
            {
                int ch=root<<1;
                t[ch].lazy+=t[root].lazy;
                t[ch+1].lazy+=t[root].lazy;
            }
            t[root].lazy=0;
        }
    }
    void update(int root)
    {
        int ch=root<<1;
        pushdown(ch);
        pushdown(ch+1);
        t[root].sum=t[ch].sum+t[ch+1].sum;//线段树更新值
    }
    void build(int l,int r,int root=1)
    {
        t[root].l=l,t[root].r=r;
        if(l!=r)
        {
            int mid=(l+r)>>1,ch=root<<1;
            build(l,mid,ch);
            build(mid+1,r,ch+1);
            update(root);//初始化每个节点的值为0
        }
        else t[root].sum=0;//线段树更新值
    }
    void change(int l,int r,ll delta,int root=1)
    {
        if(l==t[root].l&&r==t[root].r)
        {
            t[root].lazy+=delta;
            pushdown(root);
            return;
        }
        int mid=(t[root].l+t[root].r)>>1,ch=root<<1;
        if(r<=mid)change(l,r,delta,ch);
        else if(l>mid)change(l,r,delta,ch+1);
        else {change(l,mid,delta,ch);change(mid+1,r,delta,ch+1);}
        update(root);
    }
    ll sum(int l,int r,int root=1)
    {
        pushdown(root);
        if(t[root].l==l&&t[root].r==r){return t[root].sum;}
        int mid=(t[root].l+t[root].r)>>1;
        int ch=root<<1;
        if(r<=mid)return sum(l,r,ch);
        else if(l>mid)return sum(l,r,ch+1);
        else return sum(l,mid,ch)+sum(mid+1,r,ch+1);
    }
}pre,suf;

int main()
{
//    freopen("in.txt","r",stdin);
    cin>>n;scanf("%s",s+1);
    pre.build(1,n);
    suf.build(1,n);
    for(int i=1;i<=n;i++)
        if(s[i]=='1')
        {
            ans=(ans+pre.sum(1,i))%mod;
            if(i!=n)pre.change(i+1,n,1);
            if(i!=1)suf.change(1,i-1,1);
        }
    printf("%lld\n",ans);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&q,&x);
        ll pres=pre.sum(1,x);
        ll sufs=suf.sum(x,n);
        if(q==1)
        {
            ans=(ans+pres+sufs)%mod;
            if(x!=n)pre.change(x+1,n,1);
            if(x!=1)suf.change(1,x-1,1);
        }
        else
        {
            ans=((ans-pres-sufs)%mod+mod)%mod;
            if(x!=n)pre.change(x+1,n,-1);
            if(x!=1)suf.change(1,x-1,-1);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值