問題ツリーラインの概要

セグメントツリー

タスク1

静的メンテナンス動作シーケンス:\(1 \ n型のLeq、M \ ^のLeq 10 5、-15 007 \のLeq a_iを\のLeq 15007 \)

  • LR:最大連続呼び掛けゾーンとサブセグメント、即ち求め{iの当量j個の\の当量rを\ Lの\の当量} \のlimits_ $ \最大\ sum_ {k = I} ^ J a_k $

セクションには、4つの連続する数字とサブセグメントを設けてもよいです。

  • \(合計\)メンテナンス間隔と
  • \(LMAX \)左端に近い範囲を維持し、最大連続サブセグメント
  • \(RMAX \)サブセグメント及び最大連続の近距離右端を維持
  • \(RET \)間隔と連続サブセグメント。

以下のように、それぞれ、維持することができます。

  • \(SUM_X = sum_ {LSON} + sum_ {rson} \)
  • \(lmax_x = MAX(lmax_ {LSON}、sum_ {LSON} + {lmax_ rson})\)
  • \(rmax_x = MAX(rmax_ {rson}、sum_ {rson} + {rmax_ LSON})\)
  • \(Ret_x = MAX(ret_ LSON {}、{ret_のrson}、{rmax_のLSON} + {lmax_のrson})\)

合併の同じ情報時間。

# include<bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e5+10;
struct Segment_Tree{
    int sum,lmax,rmax,ret;
}tr[N<<2];
int a[N],n;
# define ls (x<<1)
# define rs ((x<<1)+1)
# define lson (x<<1),l,mid
# define rson ((x<<1)+1),mid+1,r
# define mid ((l+r)>>1)
void build(int x,int l,int r)
{
    if (l==r) {
        tr[x].sum=tr[x].lmax=tr[x].rmax=tr[x].ret=a[l];
        return;
    }
    build(lson); build(rson);
    tr[x].sum=tr[ls].sum+tr[rs].sum;
    tr[x].lmax=max(tr[ls].lmax,tr[ls].sum+tr[rs].lmax);
    tr[x].rmax=max(tr[rs].rmax,tr[rs].sum+tr[ls].rmax);
    tr[x].ret=max(max(tr[ls].ret,tr[rs].ret),tr[ls].rmax+tr[rs].lmax);
}
Segment_Tree query(int x,int l,int r,int opl,int opr)
{
    if (opl<=l&&r<=opr) return tr[x];
    if (opr<=mid) return query(lson,opl,opr);
    if (opl>mid) return query(rson,opl,opr);
    Segment_Tree lo=query(lson,opl,mid), ro=query(rson,mid+1,opr) ,ans;
    ans.sum=lo.sum+ro.sum;
    ans.lmax=max(lo.lmax,lo.sum+ro.lmax);
    ans.rmax=max(ro.rmax,ro.sum+lo.rmax);
    ans.ret=max(max(lo.ret,ro.ret),lo.rmax+ro.lmax);
    return ans;
}
signed main()
{
    scanf("%lld",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    build(1,1,n);
    int T; scanf("%lld",&T);
    while (T--) {
        int l,r; scanf("%lld%lld",&l,&r);
        printf("%lld\n",query(1,1,n,l,r).ret);
    }
    return 0;
}

タスク2

二つの配列のメンテナンス操作:\(1 \ n型のLeq、M \のLeq 10 5,0 ^ \のLeq a_iを\のLeq 18} 10 ^ {ある\)

  • 0 XYが発現\(iは[X、で\ Y] \) のすべての要素を実行するために\(= a_iを\ SQRT {a_iを} \) 下丸い)を
  • 1つのXYは聞いて提示\(\ sum_ {I = X } ^ Y a_iを\) 値。

明らかに、いくつかのルートの数が開かれた後、最終的には0/1になります丸め、および10オープンルートの数が0/1になった回、無修正操作の最大ので、我々は唯一のセグメントを定義する必要があります時間の平方根は、各インターバルの開始と最大10倍の平方根ため、時間複雑であることができる\(O(10 \回M \ログ\ n)を\)

私は、入力MD、本当に酔っていた\(X> Y軸\)が出ています!

# include <cstdio>
# include <iostream>
# include <cstring>
# include <cmath>
# define max(a,b) ((a)<(b)?(b):(a))
# define min(a,b) ((a)<(b)?(a):(b))
# define int long long
using namespace std;
const int N=1e5+10;
int Lim,n,m,a[N];
struct Segment_Tree{
    int sum,cnt; 
}tr[N<<2];
# define lson ls,l,mid
# define rson rs,mid+1,r
# define mid ((l+r)>>1)
# define ls (x<<1)
# define rs (x<<1|1)
void clear()
{
    memset(tr,0,sizeof(tr));
    memset(a,0,sizeof(a));
    Lim=0;
}
void build(int x,int l,int r)
{
    if (l==r) { tr[x].sum=a[l]; tr[x].cnt=0; return;}
    build(lson); build(rson);
    tr[x].sum=tr[ls].sum+tr[rs].sum;
}
void update(int x,int l,int r,int opl,int opr)
{
    if (tr[x].cnt>=Lim) return;
    if (opl<=l&&r<=opr) tr[x].cnt++;
    if (l==r) { tr[x].sum=sqrt(tr[x].sum); return;}
    if (opl<=mid) update(lson,opl,opr);
    if (opr>mid) update(rson,opl,opr);
    tr[x].sum=tr[ls].sum+tr[rs].sum;
}
int query(int x,int l,int r,int opl,int opr)
{
    if (opl<=l&&r<=opr) return tr[x].sum;
    int ret=0;
    if (opl<=mid) ret+=query(lson,opl,opr);
    if (opr>mid) ret+=query(rson,opl,opr);
    return ret;
}
signed main()
{
    int T=0;
    while (~scanf("%lld",&n)) {
        printf("Case #%lld:\n",++T);
        int Max=0; for (int i=1;i<=n;i++) scanf("%lld",&a[i]),Max=max(Max,a[i]);
        while (Max!=0&&Max!=1) Lim++,Max=sqrt(Max);
        build(1,1,n); scanf("%lld",&m);
        for (int i=1;i<=m;i++) {
            int op,l,r; scanf("%lld%lld%lld",&op,&l,&r);
            if (l>r) swap(l,r);
            if (!op) update(1,1,n,l,r);
            else printf("%lld\n",query(1,1,n,l,r));
        } clear();
        puts("");
    }
    return 0;
}

タスク3

インサートの数をサポートするデータ構造を維持し、番号を削除し、すべての数字の中央値を見つけます。$1≤n≤10^ 4、1 \当量T \当量100,0 \当量a_iを\当量10 ^ 9 $

その後、すべての離散的な数、および範囲のセグメントツリーを構築。

番号を挿入し、直接更新のリーフノードツリーラインの範囲で番号を削除します。

どこへ行くのツリーラインを持つクエリは、父より息子は現在、残りのランキングkを通過対応するノードを見つけるために、彼の父より息子も大きい、右、左。

全体的な複雑さの$ O(TNログn)は$

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e4+10;
int n,tmp[N],T; queue<int>dq;
struct Segment_Tree{
    int cnt;
}tr[N<<2];
struct Qes{
    int op,x;
}q[N];
# define lson ls,l,mid
# define rson rs,mid+1,r
# define mid (l+r>>1)
# define ls (x<<1)
# define rs (x<<1|1)
void clear()
{
    memset(tr,0,sizeof(tr)); memset(q,0,sizeof(q));
    memset(tmp,0,sizeof(tmp)); 
    while (dq.size()) dq.pop();
}
void insert(int x,int l,int r,int pos)
{
    if (l==r) { tr[x].cnt++; return;}
    if (pos<=mid) insert(lson,pos);
    else insert(rson,pos);
    tr[x].cnt=tr[ls].cnt+tr[rs].cnt;
}
void erase(int x,int l,int r,int pos)
{
    if (l==r) { tr[x].cnt--; return;}
    if (pos<=mid) erase(lson,pos);
    else erase(rson,pos);
    tr[x].cnt=tr[ls].cnt+tr[rs].cnt;
}
int query(int x,int l,int r,int k)
{
    if (l==r) return l;
    if (k<=tr[ls].cnt) return query(lson,k);
    else return query(rson,k-tr[ls].cnt);
}
signed main()
{
    int num=0;
    while (~scanf("%d",&n)) {
        
        for (int i=1;i<=n;i++) {
            char s[10]; scanf("%s",s);
            if (s[0]=='i') scanf("%lld",&q[i].x),q[i].op=0,tmp[++tmp[0]]=q[i].x;
            else if (s[0]=='o') q[i].op=1;
            else q[i].op=2;
        }
        sort(tmp+1,tmp+1+tmp[0]);
        T=unique(tmp+1,tmp+1+tmp[0])-tmp-1;
        printf("Case #%lld:\n",++num);
        for (int i=1;i<=n;i++) {
            if (q[i].op==0) {
                int x=lower_bound(tmp+1,tmp+1+tmp[0],q[i].x)-tmp;
                dq.push(x); insert(1,1,T,x);
            } else if (q[i].op==1) {
                erase(1,1,T,dq.front());
                dq.pop();
            } else if (q[i].op==2) {
                int k=dq.size(); k=k/2+1;
                printf("%lld\n",tmp[query(1,1,T,k)]);
            }
        }
        clear();
    }
    return 0;
}

タスク4

以下のための所定の順列\(a_iを\ [1、 N] \) は、次のようにします\を(。。1 \ n型のLeq、m個の\のLeq 5 ^ 10 \)

  • 0 LR:\(私は[L、で\ R] \) 数\(a_iを\)昇順。
  • 1 LR:\(私は\ [L、 R] \) 数\(a_iを\)を降順に、

最後に、最初に頼む(のq \)を\の位置に数です。

前半答え、各解答\(ミッド\)の以下のシーケンス\(0 \)より大きいに設定されている、\(1 \)

だから我々は、ちょうどどのようにそれらの多くの間隔カウントする必要があります\(0 \) 数字\(1 \)の並べ替えを完了させます。

セグメントツリーは、クエリ間隔の完了する必要があります\(01 \)数、間隔割り当て\(01 \)をすることができます。

私たちは、位置ように見つける必要がある\(q個\)ポジション1の数を、この位置は、最小化する必要があります。

複雑$ O(Mはlog_2 ^ 2 N)$

# pragma GCC optimize(3)
# include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct Qes{
    int op,l,r;
}q[N];
int n,m,P,a[N];
struct Segment_Tree{
    int cnt0,cnt1,l,r,tag;
    Segment_Tree() { cnt0=cnt1=l=r=tag=0;}
}tr[N<<2];
# define lson ls,l,mid
# define rson rs,mid+1,r
# define ls (x<<1)
# define rs (x<<1|1)
# define mid (l+r>>1)
void up(int x) {
    tr[x].cnt0=tr[ls].cnt0+tr[rs].cnt0;
    tr[x].cnt1=tr[ls].cnt1+tr[rs].cnt1;
}
void build(int x,int l,int r,int val) {
    tr[x].l=l; tr[x].r=r; tr[x].tag=-1;
    if (l==r) { tr[x].cnt0=(a[l]<=val); tr[x].cnt1=(a[l]>val); return;}
    build(lson,val); build(rson,val);
    up(x);
}
void down(int x) {
    if (tr[x].tag==-1) return;
    if (tr[x].tag==0) {
        tr[ls].cnt0=tr[ls].r-tr[ls].l+1; tr[ls].cnt1=0; tr[ls].tag=0;
        tr[rs].cnt0=tr[rs].r-tr[rs].l+1; tr[rs].cnt1=0; tr[rs].tag=0;
    } else {
        tr[ls].cnt1=tr[ls].r-tr[ls].l+1; tr[ls].cnt0=0; tr[ls].tag=1;
        tr[rs].cnt1=tr[rs].r-tr[rs].l+1; tr[rs].cnt0=0; tr[rs].tag=1;
    }
    tr[x].tag=-1;
}
void update(int x,int l,int r,int opl,int opr,int opx) {
    if (opl<=l&&r<=opr) {
        if (opx==0) tr[x].tag=0,tr[x].cnt1=0,tr[x].cnt0=tr[x].r-tr[x].l+1;
        if (opx==1) tr[x].tag=1,tr[x].cnt0=0,tr[x].cnt1=tr[x].r-tr[x].l+1;
        return;
    }
    if (l==r) return;
    down(x);
    if (opl<=mid) update(lson,opl,opr,opx);
    if (opr>mid) update(rson,opl,opr,opx);
    up(x);
}
Segment_Tree query(int x,int l,int r,int opl,int opr) {
    if (opl<=l&&r<=opr) return tr[x];
    down(x); Segment_Tree lo,ro,ans;
    if (opl<=mid) lo=query(lson,opl,opr);
    if (opr>mid) ro=query(rson,opl,opr);
    ans.cnt0=lo.cnt0+ro.cnt0;
    ans.cnt1=lo.cnt1+ro.cnt1;
    return ans;
}
# undef lson
# undef rson
# undef ls
# undef rs
# undef mid
inline int read()
{
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
bool check(int Mid) {
    build(1,1,n,Mid); 
    for (int i=1;i<=m;i++) if (q[i].op==0) {
        Segment_Tree ans=query(1,1,n,q[i].l,q[i].r);
        update(1,1,n,q[i].l,q[i].l+ans.cnt0-1,0);
        update(1,1,n,q[i].l+ans.cnt0,q[i].r,1);
    } else {
        Segment_Tree ans=query(1,1,n,q[i].l,q[i].r);
        update(1,1,n,q[i].l,q[i].l+ans.cnt1-1,1);
        update(1,1,n,q[i].l+ans.cnt1,q[i].r,0);
    }
    Segment_Tree ans=query(1,1,n,P,P);
    return ans.cnt0;
}
int main() {
    n=read();m=read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=m;i++) q[i].op=read(),q[i].l=read(),q[i].r=read();
    P=read(); int l=1,r=n,ans=-1;
    while (l<=r) { 
        int mid=(l+r)>>1;
        if (check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

タスク5

ツリーへ$ Nの$ポイント、ポイント\(1 \)は、ツリーのルートノードと右のビットである\(a_iを\) $ M $の操作があります。

  • 1 XW:Wにおけるノードxポイント増加する権利。
  • 2 XW:全ての点の重みの一部のノードXをルートとするサブツリーは、W点の増加です。
  • 3 xが:ルートノード点およびすべての点xに正しい道を尋ねます。

100%のデータ$ 1 \当量のN、Mの\当量10 ^ 5、-10 a_iを\当量10 ^ 6 $、W ^ 6 \当量

チェーンラインで直接ツリーを分割します。肝臓は$ O(nは\ log_2 ^ 2のn)$です
私は、ツリーラインDFS何も聞いていません

# include <cstdio>
# include <iostream>
# include <cstring>
# define int long long
# define MAXN 200005
using namespace std;
typedef long long ll;
int n,m,r,val[MAXN],b[MAXN];
struct Tree{
    ll c[MAXN];
    int lowbit(int x){ return x&(-x);}
    void update(int x,int y){
        while (x<=n){
            c[x]+=y;
            x+=lowbit(x);
        }
    }
    ll query(int x){
        ll ret=0;
        while (x>0){
            ret+=c[x];
            x-=lowbit(x);
        }
        return ret;
    }
}c1,c2;
struct Edge{
    int pre,to;
}a[2*MAXN];
int head[MAXN],tot=0;
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
ll getsum(int l,int r)
{
    return (ll) c1.query(r)*r-c2.query(r)-(l-1)*c1.query(l-1)+c2.query(l-1);
}
int f[MAXN],dep[MAXN],son[MAXN],size[MAXN];
void dfs1(int u,int fa,int depth)
{
    f[u]=fa;dep[u]=depth;size[u]=1;
    for (int i=head[u];i;i=a[i].pre)
    {
        int v=a[i].to; if (v==fa) continue;
        dfs1(v,u,depth+1);
        size[u]+=size[v];
        if (size[son[u]]<size[v]) son[u]=v;
    }
}
int w[MAXN],cntw=0,top[MAXN],old[MAXN];
void dfs2(int u,int tp)
{
    w[u]=++cntw;top[u]=tp;
    old[cntw]=u;
    if (son[u]!=0) dfs2(son[u],tp);
    for (int i=head[u];i;i=a[i].pre)
    {
        int v=a[i].to; if (v==f[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
void change(int u,int v,int d)
{
    int f1=top[u],f2=top[v];
    while (f1!=f2){
        if (dep[f1]<dep[f2]) swap(f1,f2),swap(u,v);
        c1.update(w[f1],d);
        c1.update(w[u]+1,-d);
        c2.update(w[f1],d*(w[f1]-1));
        c2.update(w[u]+1,-d*w[u]);
        u=f[f1];
        f1=top[u];
    }
    if (dep[u]<dep[v]) swap(u,v);
    c1.update(w[v],d);
    c1.update(w[u]+1,-d);
    c2.update(w[v],d*(w[v]-1));
    c2.update(w[u]+1,-d*w[u]);
}
ll lca(int u,int v)
{
    int f1=top[u],f2=top[v];
    ll ret=0ll;
    while (f1!=f2){
        if (dep[f1]<dep[f2]) swap(f1,f2),swap(u,v);
        ret=ret+getsum(w[f1],w[u]);
        u=f[f1];
        f1=top[u];
    }
    if (dep[u]<dep[v]) swap(u,v);
    ret=ret+getsum(w[v],w[u]);
    return ret;
}
signed main()
{
    scanf("%lld%lld",&n,&m); r=1; 
    for (int i=1;i<=n;i++) scanf("%lld",&val[i]);
    int u,v;
    for (int i=1;i<=n-1;i++) {
        scanf("%lld%lld",&u,&v);
        adde(u,v); adde(v,u);
    }
    dfs1(r,0,0);
    dfs2(r,0);
    for (int i=1;i<=n;i++) b[i]=val[old[i]];
    for (int i=1;i<=n;i++) c1.update(i,b[i]-b[i-1]),c2.update(i,(b[i]-b[i-1])*(i-1));
    int ch,x,y,z;
    for (int i=1;i<=m;i++) {
        scanf("%lld%lld",&ch,&x);
        if (ch==1) scanf("%lld",&z),change(x,x,z);
        else if (ch==2) {
            scanf("%lld",&z);
            int l=w[x],r=w[x]+size[x]-1;
            c1.update(l,z); c1.update(r+1,-z);
            c2.update(l,z*(l-1)); c2.update(r+1,-z*r);
        } else {
             printf("%lld\n",lca(x,1));
        }
    }
    return 0;
}

タスク6

二つの配列のメンテナンス操作:\(1 \ n型のLeq、M \のLeq 10 5,0 ^ \のLeq a_iを\のLeq 18} 10 ^ {ある\)

  • Xは追加\(X \)をセットに追加されます
  • 1つのXYは聞いて提示\(\ sum_ {I = X } ^ Y a_iを\) 値。

ああ生きてそうできるよう書かれたタイトルの言葉!

#pragma GCC optimize(3)
# include <bits/stdc++.h>
using namespace std;
vector<int>a;
int n;
char s[10];
int main(){
    scanf("%d",&n); int x;
    while (n--) {
        scanf("%s",s);
        if (s[0]=='a') cin>>x,a.insert(lower_bound(a.begin(),a.end(),x),x);
        else if (s[0]=='d') cin>>x,a.erase(lower_bound(a.begin(),a.end(),x));
        else { long long ret=0; for (int i=2;i<a.size();i+=5) ret=ret+a[i]; printf("%lld\n",ret);}
    }
    return 0;
}

タスク7

、要求の数を与える\(L_iを、R_iを、Q_I \)を表す\([L_iを] \& [L_iを+ 1] \&... \&[R_iを] = Q_I \)を

そこの配列である場合\(\)出力ライン(\ \ YES)は、その後、正規アレイを出力する。それ以外の場合、出力ライン(\ \ NO)

データの100%に\(\当量のn個の\当量10 ^ 5、Q_I \当量2 ^ {30} \)

各バイナリは、各ビットについて、それぞれの処理を引き出すことができます

間隔場合\(L、Rの\)が $ 1と$値の範囲は、この範囲は、すべてのなければならないこと\(1 \)

だから我々は、直接、この区間に割り当てられている\(1 \)することができます。

その後された外観の合法性を決定するために、各チェックのために再びそれを行うには頼む(0 \)\位置に\(L、Rの\)すべてではありません\(1 \) その後、前後に、それは1であるが、一度競合出力\(\ NO)

上記差動プレフィックスとメンテナンスを維持するために使用することができます。複雑さがある(O(N \ log_2の\を\ \)のn)

# include<bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e5+10;
int n,Q,c[31][N],ans[31][N];
struct rec{ int l,r,d; }q[N];
void update(int l,int r,int d)
{
    for (int i=30;i>=0;i--)
        if (d&(1ll<<i)) c[i][l]++,c[i][r+1]--;
}
void init()
{
    for (int i=0;i<=30;i++) for (int j=1;j<=n;j++) c[i][j]=c[i][j-1]+c[i][j];
    for (int i=0;i<=30;i++) for (int j=1;j<=n;j++) c[i][j]=(c[i][j]>0);
    memcpy(ans,c,sizeof(c));
    for (int i=0;i<=30;i++) for (int j=1;j<=n;j++) c[i][j]=c[i][j-1]+c[i][j];
}
bool check(int l,int r,int d)
{
    for (int i=30;i>=0;i--)
     if ((!(d&(1ll<<i)))&&(c[i][r]-c[i][l-1]==r-l+1)) return false;
    return true;
}
signed main()
{
    scanf("%lld%lld",&n,&Q);
    for (int i=1;i<=Q;i++) {
        scanf("%lld%lld%lld",&q[i].l,&q[i].r,&q[i].d);
        update(q[i].l,q[i].r,q[i].d);
    }
    init();
    for (int i=1;i<=Q;i++)
        if (!check(q[i].l,q[i].r,q[i].d)) { puts("NO"); return 0; }
    puts("YES");
    for (int i=1;i<=n;i++) {
        int ret=0;
        for (int j=0;j<=30;j++) if (ans[j][i]) ret+=(1<<j);
        printf("%lld ",ret);
    }
    return 0;
}

タスク8

得られる\(N \)デジタルアレイ\(a_iを\)連続的に分割する\(k個\)セグメントを、この期間の値の各セグメントは、マイルの異なる数の数であり、最大値はどのくらい尋ねることです。

データの100%に\(N \の当量35000、K \当量分(N、50)\)

\([I]、[J fを ] \) 前方を表す\(Iは\)配列番目のに分割されている\(J \)セクションおよび最大値。

最大の\ limits_ \の$ F [I] [J] = {J-1 \当量のK \当量I-1} {F [k]は[J-1] + W(K + 1、J)} $

前記\(W(L、R) \) 間隔を表す\を([L、R] \ ) の値の一意の番号。

最適化は、$ \最大の\ limits_ {J-1 \当量のK \当量I-1} {F [k]は[J-1] W +(K + 1、J)} $を求めて検討します

まず、アウターループ列挙\(J \) 内部ループ列挙\(Iは\) 割り当てられた最初の間隔\([K] [J F -1] \)

各数値は、最初に出現する前の位置を記録し([I] \ POS)\各番号のための\(私は\)する([POS [I] + \ 1、I-1] \) を生成\(1 \)を拠出金は、ダイレクト・ラインセグメントツリーは間隔を追加することができます。

利用可能な転送セグメントツリーメンテナンスの複雑\(O(NK \ log_2 N )\)

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e5+10,M=55;
int a[N],w[N],pos[N],pre[N],n,k;
int f[N][M];
struct Segment_Tree{
    int tag,mx;
}tr[N<<2];
# define lson ls,l,mid
# define rson rs,mid+1,r
# define mid (l+r>>1)
# define ls (x<<1)
# define rs (x<<1|1)
void build(int x,int l,int r)
{
    tr[x].tag=0; tr[x].mx=0;
    if (l==r) { tr[x].mx=w[l]; return;}
    build(lson); build(rson);
    tr[x].mx=max(tr[ls].mx,tr[rs].mx);
}
void down(int x)
{
    if (!tr[x].tag) return;
    tr[ls].mx+=tr[x].tag; tr[rs].mx+=tr[x].tag;
    tr[ls].tag+=tr[x].tag; tr[rs].tag+=tr[x].tag;
    tr[x].tag=0;
}
void update(int x,int l,int r,int opl,int opr,int d)
{
    if (opl<=l&&r<=opr) { tr[x].tag+=d; tr[x].mx+=d; return;}
    down(x);
    if (l==r) return;
    if (opl<=mid) update(lson,opl,opr,d);
    if (opr>mid) update(rson,opl,opr,d);
    tr[x].mx=max(tr[ls].mx,tr[rs].mx);
}
int query(int x,int l,int r,int opl,int opr)
{
    if (opl<=l&&r<=opr) return tr[x].mx;
    down(x);
    int ret=0;
    if (opl<=mid) ret=max(ret,query(lson,opl,opr));
    if (opr>mid) ret=max(ret,query(rson,opl,opr));
    return ret;
}
signed main()
{
    scanf("%lld%lld",&n,&k);
    for (int i=1;i<=n;i++) {
        scanf("%lld",&a[i]);
        pos[i]=pre[a[i]];
        pre[a[i]]=i;
    }
    for (int j=1;j<=k;j++) {
        for (int i=1;i<=n;i++) w[i]=f[i-1][j-1];
        build(1,1,n);
        for (int i=1;i<=n;i++) {
            update(1,1,n,pos[i]+1,i,1);
            f[i][j]=query(1,1,n,1,i);
        }
    }
    printf("%lld\n",f[n][k]);
    return 0;
}

タスク9

一つ含む\(N- \)ツリーノードは、次の2つの操作を維持します:

  • 1つのUVは、右ノードU + V、彼の息子の右のポイントノード-v、彼の息子のポイント右+ V ...葉ノードまでの息子を指すと述べました。
  • 2 U Uノードが受ける権利の点を表します

データの100%\(1個の\当量のN、Mの \の当量2 \回10 ^ 5 \)

DFSツリーノードのシーケンスが連続しています。我々は、アレイ維持(Cは、[U]は\)\のノードを表す(U \)\インクリメント。

この\(C [U] \)処理アレイは、比較的簡単で、直接シーケンスは区間プラスに対応するツリーをDFS。

深さでの操作奇数と異なるノード数が偶数なら、貢献は答えに反するだろう。

深さが奇数の記録である場合には記録の量を減らすために増加した量は、さえも深さです。

最終的な答えは、プラスまたはマイナスの最初の貢献です。

複雑さがある(O(N \ log_2の\を\ \)のn)

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=2e5+10;
struct rec{ int pre,to; }a[N<<1];
int L[N],R[N],c[N],v[N],head[N],dep[N];
int n,tot,cnt,m;
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
void dfs(int u,int fa)
{
    L[u]=++cnt; dep[u]=dep[fa]+1;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        dfs(v,u);
    }
    R[u]=cnt;
}
# define lowbit(x) (x&(-x))
void update(int x,int y){for (;x<=n;x+=lowbit(x)) c[x]+=y;}
int query(int x){int ret=0; for (;x;x-=lowbit(x)) ret+=c[x]; return ret;}
void modify(int l,int r,int d){update(l,d); update(r+1,-d);}
signed main()
{
    scanf("%lld%lld",&n,&m);
    for (int i=1;i<=n;i++) scanf("%lld",&v[i]);
    for (int i=1;i<n;i++) {
        int u,v; scanf("%lld%lld",&u,&v);
        adde(u,v); adde(v,u);
    }
    dfs(1,0);
    for (int i=1;i<=m;i++) {
        int op,x; scanf("%lld%lld",&op,&x);
        if (op==1) {
            int d; scanf("%lld",&d);
            if (dep[x]&1) modify(L[x],R[x],d);
            else modify(L[x],R[x],-d);
        } else {
            if (dep[x]&1) printf("%lld\n",v[x]+query(L[x]));
            else printf("%lld\n",v[x]-query(L[x]));
        }
    }
    return 0;
}

おすすめ

転載: www.cnblogs.com/ljc20020730/p/11291465.html