문제 트리 라인 요약

세그먼트 트리

작업 1

정적 유지 동작 시퀀스 : \. (1 \ n-당량, m \ ^ 5 당량 10 -15 007 \ 당량 A_I \ 당량 15,007 \) ,

  • LR : 최대 연속 심문 영역 및 하위 부분, 즉 요구 {제가 당량 J의 \의 당량의 R을 \ 패 \의 당량} \의 limits_ $ \ 최대 \ sum_ {K = 1} ^ J a_k $

제 네 개의 연속 부호 서브 세그먼트를 구비 할 수있다 :

  • \ SUM (\) 유지 보수주기 및
  • \ (Lmax를 \) 왼쪽 끝에 가까운 거리를 유지하고, 최대 연속 서브 세그먼트
  • \ (Rmax가 \) 서브 세그먼트 근거리 우단 연속 최대 유지
  • \ (RET \) 구간과 연속 서브 세그먼트.

다음과 같이 각각 유지 될 수있다 :

  • \ (sum_x = sum_ {LSON} + {sum_ rson} \)
  • \ (lmax_x = 최대 (lmax_ LSON {}, {sum_ LSON} + {lmax_ rson}) \)
  • \ (rmax_x = 최대 (rmax_ rson {}, {sum_ rson} + {rmax_ LSON}) \)
  • \ (Ret_x = 최대 (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

두 시퀀스에서 유지 보수 작업 : (\. \ n-당량, m의 \의 10 당량 5,0 ^ \ 당량 A_I \ 1 당량 18 ^ {10} \)

  • 0 XY 표현 \ (나는 [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을 \)

정말 술에 취해, 메릴랜드 입력 \ (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-당량, m의 \ 당량 5 ^ 10 \)

  • 0 LR 다음 \ (ⅰ [1,5에 \ R] \) 번호 \ (A_I \) 오름차순;
  • LR 한 다음 \ (난에 \ [L, R] \) 번호 \ (A_I \) 내림차순;

마지막으로 첫 번째 질문 (Q의 \) \의 위치에 번호입니다.

전반 답변 각 않음 \ (MID \) 는 이하의 순서 \ (0 \) 보다 크게 설정되고, \ (1 \) .

그래서 우리는 단지 간격 그들 중 얼마나 많은 계산이 필요 \ (0 \)를 숫자 \ (1 \)는 정렬을 완료합니다.

세그먼트 나무는 조회 간격 완료해야합니다 (01 \) \ 수, 간격 할당 \ (01 \)가 될 수 있습니다.

우리는되도록 위치를 찾을 필요가 \ (Q의 \) 위치 1에 번호,이 위치를 최소화 할 필요가있다.

$ 복잡도 O (m ^ 2의 log_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 $ 작업이 있습니다 :

  • XW 1 : w에서 노드 X 포인트 증가 오른쪽.
  • 2 XW : 모든 지점의 무게의 일부 노드 X를 루트로하는 서브 트리 승 점 증가합니다.
  • 3 ×는 : 루트 노드 점과 모든 점 X에 대한 권리 경로를 부탁드립니다.

100 %의 데이터 $ 1 \ 당량의 N, M \ 당량의 10 ^ 5, -10 A_I \ 당량 10 ^ 6 $, w ^ 6 \ 당량

체인 라인에 직접 나무를 분할합니다. 간은 $ O (n \ log_2 N ^ 2) 인 $
나는 나무 라인 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

두 시퀀스에서 유지 보수 작업 : (\. \ n-당량, m의 \의 10 당량 5,0 ^ \ 당량 A_I \ 1 당량 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는 \) 대표 \을 (a [l_i] \ 및 A [l_i + 1] \ & ... \ & A [r_i = q_i \)

이 배열의 경우 \ (a \) 의 출력 라인 \는 (\ 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 \) 연속으로 나눌 \ (케이 \) 부분을,이 기간의 값의 각각의 세그먼트는 마일 상이한 수의 수이고, 최대 값이 얼마나 많이 요구한다.

데이터의 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는 \) , 다음 제 간격이 할당 \ (F [K] [J -1] \)

최초로 출현 전에 각 번호 기록 위치 \는 (POS가 [I]가 \) 각 번호 \ (I는 \)(\ [POS는 [내가] + 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- \) 트리 노드, 다음과 같은 두 가지 작업을 유지 :

  • 1 자외선은 포인트 것이라고 말했다 올바른 노드 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