Codeforces Round #489 (Div. 2) E. Nastya and King-Shamans

题意

一个序列
要你支持单点修改
然后每一次问你是否存在一个位置,使得这个位置的值等于他前一位的前缀和

分块!

如果我们把每一个位置上的值前去他前面所有的数,那么问题就等价于找是否存在0
那么问题就是要支持区间修改,然后询问是否存在0这个数字
容易想到分块维护。和loj6187的维护方法是一样的
在块内维护一个hash表。
时间复杂度可以视为是 O(qn) O ( q n )
然而这样T了。可能常数较大
卡了一手常但是并没有卡过去

寻找别的方法!

所谓的寻找别的方法其实就是看题解
我们考虑,不这么转化模型。。
我们把模型转化成,找一个 sumi=sumi12 s u m i = s u m i − 1 ∗ 2 ,其中sum表示前缀和
发现这个*2的形式
想到可以暴力跳。。
考虑到sum是递增的,那么我们现在在一个点 i i ,那么答案必须满足sumx>=sumi2
发现这样最多只会跳 logai l o g a i
于是用树状数组来维护即可
跳的过程用二分加速
其实如果你用倍增的话,应该会更快
时间复杂度是 O(nlog2nloga) O ( n l o g 2 n l o g a )
看起来并不比分块优秀,但是常数较小,所以跑得飞快

分块的代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=200005;
const int P=100003;
int p,x;
int n,q;
LL a[N];
LL g[N];
int get_hash(LL x)  {x<<=1;return (x%P+P)%P;}
int read ()
{
    char ch=getchar();int x=0;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9')    {x=x*10+ch-'0';ch=getchar();}
    return x;
}
bool tf;
int ans;
struct qq
{
    int cnt;
    LL lzy;
    int q[450],ed;
    int last[P],lst[450];
    pair<LL,int> s[450];
    qq () {lzy=0;cnt=0;ed=0;}
    void insert (int x,int id)
    {
        //printf("insert:%d %d\n",x,id);
        int xx=get_hash(x);
        int now;
        if (ed!=0)  now=q[ed--];
        else now=++cnt;
        s[now].first=x;s[now].second=id;
        lst[now]=last[xx];last[xx]=now;
    }
    void del (int x,int id)
    {
        int xx=get_hash(x);
    //  printf("del:%d %d %d\n",x,xx,id);
        int ll=0,now;
        for (int u=last[xx];u!=0;u=lst[u])
        {
            if (s[u].first==x&&s[u].second==id) {now=u;break;}
            ll=u;
        }
        //printf("%d %d\n",now,ll);
        if (ll==0) last[xx]=lst[now];
        else lst[ll]=lst[now];
        q[++ed]=now;
    }
    void find (LL x)
    {
        x-=lzy;
        int xx=get_hash(x);
        for (int u=last[xx];u!=0;u=lst[u])
            if (s[u].first==x)
            {
                tf=true;
                ans=s[u].second;
                return ;
            }
    }
}T[450];
int siz;
void change (int l,int r,LL x)//这一段加这么多 
{
    if (l>r) return ;
//  printf("l:%d r:%d x:%d\n",l,r,x);
    int L=l/siz,R=r/siz;
    if (L==R)
    {
        for (int u=l;u<=r;u++)
        {
            T[L].del(g[u],u);
            g[u]+=x;
            T[L].insert(g[u],u);
        }
        return ;
    }
    for (int u=L+1;u<R;u++) T[u].lzy+=x;
    //printf("begin:\n");
    for (int u=l;u<(L+1)*siz;u++)
    {
        //printf("%d\n",u);
        T[L].del(g[u],u);
    //  printf("next:");
        g[u]+=x;
        T[L].insert(g[u],u);
    }
    //printf("mid\n");
    for (int u=R*siz;u<=r;u++)
    {
        T[R].del(g[u],u);
        g[u]+=x;
        T[R].insert(g[u],u);
    }
    //printf("end\n");
}
void solve ()
{
    if (ans!=-1)    return ;
    tf=false;
    int nn=n/siz;
    ans=-1;
    int xx=p/siz;
    for (int u=xx;u<=nn;u++)
    {
        T[u].find(0);
        if (tf) break;
    }
}
int main()
{
    n=read();q=read();siz=sqrt(n);
    for (int u=1;u<=n;u++)a[u]=read();
    int sum=0;
    for (int u=1;u<=n;u++) 
    {
        g[u]=a[u]-sum;
        T[u/siz].insert(a[u]-sum,u);
        sum=sum+a[u];
    }
    /*for (int u=1;u<=n;u++)    printf("%d ",g[u]);
    printf("\n");*/
    ans=-1;
    solve();
    for (int u=1;u<=q;u++)
    {
        p=read();x=read();
        change(p,p,x-a[p]);
        change(p+1,n,a[p]-x);
        if (p<=ans) ans=-1;
        solve();
        a[p]=x;
        printf("%d\n",ans);
    }
    return 0;
}

log^3的代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=200005;
int n,q;
int a[N];
LL f[N];
int lb (int x)  {return x&(-x);}
void change (int x,int y)   {while (x<=n){f[x]=f[x]+y;x+=lb(x);}}
LL get (int x)  {LL lalal=0;while (x>0) {lalal=lalal+f[x];x-=lb(x);}return lalal;}
void solve ()
{
    int now=1;
    LL now1=get(1);
    if (now1==0)    {printf("1\n");return ;}
    while (now<=n)
    {
        int l=now+1,r=n,lalal=-1;
        while (l<=r)
        {
            int mid=(l+r)>>1;
            LL x=get(mid);
            if (x>=now1*2)  {lalal=mid;r=mid-1;}
            else l=mid+1;
        }
        if (lalal==-1) break;
        now=lalal;now1=get(lalal);
        if (now1==get(lalal-1)*2)
        {
            printf("%d\n",lalal);
            return ;
        }
    }
    printf("-1\n");
}
int read ()
{
    char ch=getchar();int x=0;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9')    {x=x*10+ch-'0';ch=getchar();}
    return x;
}
int main()
{
    n=read();q=read();
    for (int u=1;u<=n;u++)  
    {
        a[u]=read();
        change(u,a[u]);
    }
    for (int u=1;u<=q;u++)
    {
        int x,p;
        p=read();x=read();
        change(p,x-a[p]);a[p]=x;
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值