Loading

7.20 学习笔记

7.20 学习笔记

1 线段树应用

1.1 2019 ICPC Shanghai Onsite F

链接

我们只需要把握好打标记得顺序以及标记的先后即可。应该是先推覆盖标记,然后是乘法标记,最后是加法标记。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
// #define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;
const int mod=1e9+7;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

struct edge{
    int to,next;
    inline void intt(int to_,int ne_){
        to=to_;next=ne_;
    }
};
edge li[N<<1];
int head[N],tail;

inline void add(int from,int to){
    li[++tail].intt(to,head[from]);
    head[from]=tail;
}

int fa[N],deep[N],siz[N],top[N],son[N],tot,id[N],rk[N];

inline void dfs1(int k,int fat){
    fa[k]=fat;deep[k]=deep[fat]+1;siz[k]=1;
    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(to==fat) continue;
        dfs1(to,k);
        siz[k]+=siz[to];
        if(siz[son[k]]<siz[to]) son[k]=to;
    }
}

inline void dfs2(int k,int t){
    top[k]=t;id[k]=++tot;rk[tot]=k;
    if(son[k]) dfs2(son[k],t);
    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(to==fa[k]||to==son[k]) continue;
        dfs2(to,to);
    }
}

int t,n,a[N],m;

struct node{
    int AddLazy,MulLazy,Sum,SquSum,CubSum,CovLazy,len;
    inline void Clear(){
        AddLazy=Sum=SquSum=CubSum=len=0;
        MulLazy=1;CovLazy=-1;
    }
};

#define ls k<<1
#define rs k<<1|1
struct DS{
    node p[N<<4];
    inline void PushUp(int k){
        p[k].len=p[ls].len+p[rs].len;
        p[k].Sum=(p[ls].Sum+p[rs].Sum)%mod;
        p[k].SquSum=(p[ls].SquSum+p[rs].SquSum)%mod;
        p[k].CubSum=(p[ls].CubSum+p[rs].CubSum)%mod;
    }
    inline void Build(int k,int l,int r){
        if(l==r){
            p[k].len=1;p[k].Sum=a[rk[l]];p[k].SquSum=(1ll*a[rk[l]]*a[rk[l]])%mod;
            p[k].CubSum=(1ll*a[rk[l]]*a[rk[l]]%mod*a[rk[l]])%mod;return;
        }
        p[k].Clear();
        int mid=(l+r)>>1;
        Build(ls,l,mid);Build(rs,mid+1,r);
        PushUp(k);
    }
    inline void A(int k,int x){
        p[k].Sum=1ll*x*p[k].len%mod;p[k].SquSum=1ll*p[k].len*x%mod*x%mod;
        p[k].CubSum=1ll*x*x%mod*x%mod*p[k].len%mod;p[k].CovLazy=x;
        p[k].AddLazy=0;p[k].MulLazy=1;
    }//cover the firstest lazy to push down
    inline void B(int k,int x){
        p[k].Sum=1ll*p[k].Sum*x%mod;p[k].SquSum=1ll*p[k].SquSum*x%mod*x%mod;
        p[k].CubSum=1ll*p[k].CubSum*x%mod*x%mod*x%mod;
        p[k].MulLazy=1ll*p[k].MulLazy*x%mod;p[k].AddLazy=1ll*p[k].AddLazy*x%mod;
    }//mul the secord lazy to push down
    inline void C(int k,int x){
        p[k].CubSum=(1ll*(p[k].CubSum+(1ll*3*x%mod*p[k].SquSum)%mod)%mod+(1ll*3*x%mod*x%mod*p[k].Sum)%mod+(1ll*x*x%mod*x%mod*p[k].len)%mod)%mod;
        p[k].SquSum=(p[k].SquSum+1ll*2*x%mod*p[k].Sum%mod+1ll*x*x%mod*p[k].len%mod)%mod;
        (p[k].Sum+=1ll*p[k].len*x%mod)%=mod;(p[k].AddLazy+=x)%=mod;
    }//add the last lazyto
    inline void PushDown(int k){
        if(p[k].CovLazy!=-1){
            A(ls,p[k].CovLazy);A(rs,p[k].CovLazy);
            p[k].CovLazy=-1;
        }
        if(p[k].MulLazy!=1){
            B(ls,p[k].MulLazy);B(rs,p[k].MulLazy);
            p[k].MulLazy=1;
        }
        if(p[k].AddLazy){
            C(ls,p[k].AddLazy);C(rs,p[k].AddLazy);
            p[k].AddLazy=0;
        }
    }
    inline void ChangeCover(int k,int l,int r,int z,int y,int x){
        if(l==z&&r==y){
            A(k,x);return;
        }
        int mid=(l+r)>>1;
        PushDown(k);
        if(y<=mid) ChangeCover(ls,l,mid,z,y,x);
        else if(z>mid) ChangeCover(rs,mid+1,r,z,y,x);
        else ChangeCover(ls,l,mid,z,mid,x),ChangeCover(rs,mid+1,r,mid+1,y,x);
        PushUp(k);
    }
    inline void ChangeMul(int k,int l,int r,int z,int y,int x){
        if(l==z&&r==y){
            B(k,x);return;
        }
        int mid=(l+r)>>1;
        PushDown(k);
        if(y<=mid) ChangeMul(ls,l,mid,z,y,x);
        else if(z>mid) ChangeMul(rs,mid+1,r,z,y,x);
        else ChangeMul(ls,l,mid,z,mid,x),ChangeMul(rs,mid+1,r,mid+1,y,x);
        PushUp(k);
    }
    inline void ChangeAdd(int k,int l,int r,int z,int y,int x){
        if(l==z&&r==y){
            C(k,x);return;
        }
        int mid=(l+r)>>1;
        PushDown(k);
        if(y<=mid) ChangeAdd(ls,l,mid,z,y,x);
        else if(z>mid) ChangeAdd(rs,mid+1,r,z,y,x);
        else ChangeAdd(ls,l,mid,z,mid,x),ChangeAdd(rs,mid+1,r,mid+1,y,x);
        PushUp(k);
    }
    inline int AskCubSum(int k,int l,int r,int z,int y){
        if(l==z&&r==y) return p[k].CubSum;
        int mid=(l+r)>>1;
        PushDown(k);
        if(y<=mid) return AskCubSum(ls,l,mid,z,y);
        else if(z>mid) return AskCubSum(rs,mid+1,r,z,y);
        else return (AskCubSum(ls,l,mid,z,mid)+AskCubSum(rs,mid+1,r,mid+1,y))%mod;
    }
};
DS ds;

inline void ChangeCover(int a,int b,int x){
    while(top[a]!=top[b]){
        if(deep[top[a]]<deep[top[b]]) swap(a,b);
        ds.ChangeCover(1,1,tot,id[top[a]],id[a],x);
        a=fa[top[a]];
    }
    if(id[a]>id[b]) swap(a,b);
    ds.ChangeCover(1,1,tot,id[a],id[b],x);
}

inline void ChangeAdd(int a,int b,int x){
    while(top[a]!=top[b]){
        if(deep[top[a]]<deep[top[b]]) swap(a,b);
        ds.ChangeAdd(1,1,tot,id[top[a]],id[a],x);
        a=fa[top[a]];
    }
    if(id[a]>id[b]) swap(a,b);
    ds.ChangeAdd(1,1,tot,id[a],id[b],x);
}

inline void ChangeMul(int a,int b,int x){
    while(top[a]!=top[b]){
        if(deep[top[a]]<deep[top[b]]) swap(a,b);
        ds.ChangeMul(1,1,tot,id[top[a]],id[a],x);
        a=fa[top[a]];
    }
    if(id[a]>id[b]) swap(a,b);
    ds.ChangeMul(1,1,tot,id[a],id[b],x);
}

inline int GetAns(int a,int b){
    int ans=0;
    while(top[a]!=top[b]){
        if(deep[top[a]]<deep[top[b]]) swap(a,b);
        (ans+=ds.AskCubSum(1,1,tot,id[top[a]],id[a]))%=mod;
        a=fa[top[a]];
    }
    if(id[a]>id[b]) swap(a,b);
    (ans+=ds.AskCubSum(1,1,tot,id[a],id[b]))%=mod;
    return ans;
}

inline void Intt(){
    read(n);
    for(int i=1;i<=n-1;i++){
        int from,to;read(from);read(to);
        add(from,to);add(to,from);
    }
    for(int i=1;i<=n;i++) read(a[i]);
    dfs1(1,0);dfs2(1,1);
}

inline void Clear(){
    // memset(fa,0,sizeof(fa));
    // memset(id,0,sizeof(id));
    // memset(rk,0,sizeof(rk));
    // memset(siz,0,sizeof(siz));
    // memset(top,0,sizeof(top));
    // memset(son,0,sizeof(son));
    
    // memset(ds.p,0,sizeof(ds.p));
    // memset(li,0,sizeof(li));
    tot=0;
    for(int i=1;i<=n;i++) son[i]=0;
    tail=0;for(int i=1;i<=n;i++) head[i]=0;
}

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    for(int i=1;i<=t;i++){
        printf("Case #%d:\n",i);
        Intt();
        ds.Build(1,1,tot);
        read(m);
        for(int i=1;i<=m;i++){
            // printf("%d\n",i);
            int a,b,op;read(op);
            read(a);read(b);
            if(op==1){
                int x;read(x);
                ChangeCover(a,b,x);
            }
            else if(op==2){
                int x;read(x);
                ChangeAdd(a,b,x);
            }
            else if(op==3){
                int x;read(x);
                ChangeMul(a,b,x);
            }
            else{
                printf("%d\n",GetAns(a,b));
            }
        }
        Clear();
    }
    return 0;
}

1.2 2019 Multi-University,HDU Day 6 E

链接

主要思路是固定矩形上边界,下边界,然后做最大的右边减左边即可。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define LL long long
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
const int maxn=2009;

int n;
struct Node {
    LL Sum,Max,Lmax,Rmax;
} Tr[maxn<<2];

struct Uni {
    int num;
    LL tmp[maxn];
    void init() {
        sort(tmp+1,tmp+1+num);
        num=unique(tmp+1,tmp+1+num)-tmp-1;
    }
    void transform(LL &val) {
        val=lower_bound(tmp+1,tmp+1+num,val)-tmp;
    }
} Ux,Uy;

void PushUp(Node &Ans,Node Lef,Node Rig) {
    Ans.Sum=Lef.Sum+Rig.Sum;
    Ans.Max=max(max(Lef.Max,Rig.Max),Lef.Rmax+Rig.Lmax);
    Ans.Lmax=max(Lef.Lmax,Lef.Sum+Rig.Lmax);
    Ans.Rmax=max(Rig.Rmax,Rig.Sum+Lef.Rmax);
}

void Build(int rt=1,int l=1,int r=Uy.num) {
    Tr[rt].Sum=Tr[rt].Max=Tr[rt].Lmax=Tr[rt].Rmax=0;
    if(l==r) {
        return;
    }
    Build(ls,l,mid);
    Build(rs,mid+1,r);
}

void Update(int pos,LL val,int rt=1,int l=1,int r=Uy.num) {
    if(l==r) {
        Tr[rt].Sum=Tr[rt].Max=Tr[rt].Lmax=Tr[rt].Rmax=Tr[rt].Sum+val;
        return;
    }
    if(pos<=mid)
        Update(pos,val,ls,l,mid);
    else
        Update(pos,val,rs,mid+1,r);
    PushUp(Tr[rt],Tr[ls],Tr[rs]);
}

Node Query(int L=1,int R=Uy.num,int rt=1,int l=1,int r=Uy.num) {
    if(l>=L&&r<=R) {
        return Tr[rt];
    }
    if(R<=mid)
        return Query(L,R,ls,l,mid);
    if(L>mid)
        return Query(L,R,rs,mid+1,r);
    Node Ans,Lef=Query(L,R,ls,l,mid),Rig=Query(L,R,rs,mid+1,r);
    PushUp(Ans,Lef,Rig);
    return Ans;
}

struct Point {
    LL x,y,val;
    bool operator<(const Point &R)const{
        return x<R.x;
    }
} e[maxn];

int main() {
    // freopen("my.in","r",stdin);
    // freopen("std.out","w",stdout);
    int _;
    for(scanf("%d",&_); _--;) {
        scanf("%d",&n);
        rep(i,1,n) {
            scanf("%lld%lld%lld",&e[i].x,&e[i].y,&e[i].val);
            Ux.tmp[i]=e[i].x;
            Uy.tmp[i]=e[i].y;
        }
        Ux.num=Uy.num=n;
        Ux.init();
        Uy.init();
        rep(i,1,n) {
            Ux.transform(e[i].x);
            Uy.transform(e[i].y);
        }
        sort(e+1,e+1+n);
        LL ans=0;
        rep(i,1,Ux.num){
            Build();
            int At=1;
            while(e[At].x<i)At++;
            rep(j,i,Ux.num){
                while(At<=n&&e[At].x==j)
                    Update(e[At].y,e[At].val),++At;
                ans=max(ans,Query().Max);
            }
        }
        printf("%lld\n",ans);
    }
}

1.3 P1486 [NOI2004] 郁闷的出纳员

链接

这个东西直接就是 FHQTreap 的裸题,所有操作都可以用分裂和合并方便的实现。

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Min(T a,T b){
    return a<b?a:b;
}

inline int random(int n){
    return 1ll*rand()*rand()%n+1;
}

int n,Minn,Money;

struct node{
    int minn,val,size,key,l,r;
};

struct FHQ_Treap{
    node p[N];int tot,root;
    inline FHQ_Treap(){p[0].minn=-INF;}
    inline int NewNode(int val){
        p[++tot].val=val;p[tot].key=random(INF);
        p[tot].size=1;p[tot].minn=val;p[tot].l=p[tot].r=0;return tot;
    }
    inline void PushUp(int k){
        p[k].size=p[p[k].l].size+p[p[k].r].size+1;
        p[k].minn=Min(p[k].val,Min(p[p[k].l].minn,p[p[k].r].minn));
    }
    inline void Split(int k,int val,int &x,int &y){
        if(k==0){x=y=0;return;}
        if(p[k].val<=val){x=k;Split(p[k].r,val,p[k].r,y);}
        else{y=k;Split(p[k].l,val,x,p[k].l);}PushUp(k);
    }
    inline int Merge(int a,int b){
        if(!a||!b) return a+b;
        if(p[a].key>p[b].key){p[a].r=Merge(p[a].r,b);PushUp(a);return a;}
        else{p[b].l=Merge(a,p[b].l);PushUp(b);return b;}
    }
    inline void TreeInsert(int val){
        val-=Money;int x,y;Split(root,val,x,y);root=Merge(Merge(x,NewNode(val)),y);
    }
    inline void Delete(){
        if(p[root].minn+Money<Minn){int x,y;Split(root,Minn-Money-1,x,y);root=y;}
    }
    inline int Find(int k,int rank){
        if(p[p[k].r].size+1==rank) return p[k].val;
        else if(p[p[k].r].size+1>=rank) return Find(p[k].r,rank);
        else return Find(p[k].l,rank-1-p[p[k].r].size);
    }
    inline int TreeFind(int rank){
        if(p[root].size<rank) return -1;else return Find(root,rank)+Money;
    }
    inline int GetCnt(){
        return tot-p[root].size;
    }
};
FHQ_Treap tr;

inline char Get(){
    char c=getchar();while(c<'A'||c>'Z') c=getchar();return c;
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(Minn);
    for(int i=1;i<=n;i++){
        char c;int k;c=Get();read(k);
        if(c=='I'){if(k>=Minn) tr.TreeInsert(k);}
        else if(c=='A') Money+=k;
        else if(c=='S') Money-=k;
        else if(c=='F') printf("%d\n",tr.TreeFind(k));
        tr.Delete();
    }
    printf("%d\n",tr.GetCnt());
    return 0;
}

1.4 2019 Multi-University,HDU Day 3 D

链接

我们考虑二分这个值,二分的上边界就是所有负数之和,二分的下边界是所有的正数之和。显而易见这个二分的上下边界是对的,且答案具有可二分性。

假设我们当前二分的值为 \(mid\)​,那么我们要保证的是所有段的最大值要小于这个 \(mid\)​。我们考虑在满足左边条件的前提下看看每一个前缀能够分为多少段。设 \(f_i\) 为以 \(i\) 为结尾的前缀在满足条件的情况下最多能分多少段。如果 \(f_i\ge k\),那么就说明 \(mid\) 是合法的,因为即使大于 \(k\) 的话,我们仍然可以舍弃后面若干段来达到 \(k\) 段。

我们考虑用 dp 来计算 \(f\)​ 数组,不难发现转移式子:

\[f_i=\max\limits_{0\le k\le i-1,sum_i-sum_k\le mid} \{f_k+1\} \]

我们考虑把下面第二个式子进行移项,可以得到 \(sum_i-mid\le sum_k\) 。那么我们可以通过把 \(sum\) 数组离散化,然后把 \(sum_i-mid\) 放到总序列里二分,来得到我们的决策集合。那么在决策集合这个区间里取最值,我们可以用线段树来维护。更新完 \(f_i\) 后,把离散化后的 \(sum_i\) 这个位置改为 \(f_i\),所以我们线段树需要维护区间取最值和单点加。

这样这个题就做完了。

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
// #define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Min(T a,T b){return a<b?a:b;}
template<typename T> inline T Max(T a,T b){return a<b?b:a;}

int a[N],n,k,t,len;
ll l,r,sum[N],f[N],b[N];
map<ll,int> ID;

#define ls k<<1
#define rs k<<1|1
struct DS{
    int p[N<<4];
    inline void PushUp(int k){p[k]=Max(p[ls],p[rs]);}
    inline void BuildT(int k,int l,int r){
        if(l==r){p[k]=-INF;return;}
        int mid=(l+r)>>1;
        BuildT(ls,l,mid);BuildT(rs,mid+1,r);PushUp(k);
    }
    inline void CNode(int k,int l,int r,int w,int x){
        if(l==r){p[k]=x;return;}
        int mid=(l+r)>>1;
        if(w<=mid) CNode(ls,l,mid,w,x);
        else CNode(rs,mid+1,r,w,x);PushUp(k);
    }
    inline int GMax(int k,int l,int r,int z,int y){
        if(l==z&&r==y){
            // printf("l:%d r:%d p:%lld\n",l,r,p[k]);
            return p[k];
        }
        int mid=(l+r)>>1;
        if(y<=mid) return GMax(ls,l,mid,z,y);
        else if(z>mid) return GMax(rs,mid+1,r,z,y);
        else return Max(GMax(ls,l,mid,z,mid),GMax(rs,mid+1,r,mid+1,y));
    }
    // inline void Print(){
    //     for(int i=1;i<=len;i++){
    //         printf("i:%d val:%lld\n",i,GetMax(i,i));
    //     }
    // }
    inline void BuildTree(){BuildT(1,1,len);}
    inline void ChangeNode(int w,int x){CNode(1,1,len,w,x);}
    inline int GetMax(int l,int r){return GMax(1,1,len,l,r);}
};
DS ds;

inline void Clear(){
    ID.clear();l=r=0;
}

inline void intt(){
    Clear();
    read(n);read(k);
    for(int i=1;i<=n;i++){
        read(a[i]);
        if(a[i]<0) l+=a[i];else r+=a[i];
        sum[i]=sum[i-1]+a[i];b[i]=sum[i];
    }
    sum[++n]=0;
    sort(sum+1,sum+n+1);
    len=unique(sum+1,sum+n+1)-sum-1;
    for(int i=1;i<=len;i++) ID[sum[i]]=i;
    n--;
}

// inline void Print(){
//     for(int i=0;i<=n;i++) printf("i:%d f:%lld\n",i,f[i]);
//     for(int i=1;i<=n;i++) printf("%lld ",f[i]);puts("");
// }

inline bool Check(ll mid){
    // printf("mid:%lld\n",mid);
    ds.BuildTree();
    ds.ChangeNode(ID[b[0]],0);f[0]=0;
    // ds.Print();
    // printf("1\n");Print();
    for(int i=1;i<=n;i++){
        ll now=b[i]-mid;
        int posi=lower_bound(sum+1,sum+len+1,now)-sum;
        // printf("i:%d posi:%d\n",i,posi);
        if(posi>len){f[i]=-INF;continue;}
        f[i]=ds.GetMax(posi,len)+1;
        ds.ChangeNode(ID[b[i]],f[i]);
        if(f[i]>=k) return 1;
    }
    // printf("2\n");Print();
    return 0;
}


signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    while(t--){
        intt();
        ll ans;
        while(l<=r){
            ll mid=(l+r)>>1;
            if(Check(mid)) r=mid-1,ans=mid;
            else l=mid+1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

1.5 2019 Multi-University,HDU Day 3 G

链接

我们直接把原序列离散化以后在权值线段树上二分就可以得到答案。这里需要指出我一开始思路错误的地方,

这个答案序列并不是单调递增的。

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

inline int random(int n){return 1ll*rand()*rand()%n+1;}

int q,n,m,a[N],rk[N],len,b[N];

struct node{
    int sum,val;
};

#define ls k<<1
#define rs k<<1|1
struct DS{
    node p[N<<4];
    inline void PushUp(int k){
        p[k].sum=p[ls].sum+p[rs].sum;
        p[k].val=p[ls].val+p[rs].val;
    }
    inline void CNode(int k,int l,int r,int w,int x){
        if(l==r){p[k].val+=x;p[k].sum=p[k].val*rk[l];return;}
        int mid=(l+r)>>1;
        if(w<=mid) CNode(ls,l,mid,w,x);
        else CNode(rs,mid+1,r,w,x);PushUp(k);
    }
    inline int BFind(int k,int l,int r,int x){
        if(l==r){
            if(p[k].sum<=x) return 0;
            else return p[k].val-(x/(p[k].sum/p[k].val));
        }
        int mid=(l+r)>>1;
        if(p[ls].sum<=x) return BFind(rs,mid+1,r,x-p[ls].sum);
        else return BFind(ls,l,mid,x)+p[rs].val;
    }
    inline void Clear(){memset(p,0,sizeof(p));}
    inline void ChangeNode(int w){CNode(1,1,len,w,1);}
    inline int BinaryFind(int x){return BFind(1,1,len,x);}
};
DS ds;

inline void Clear(){
    ds.Clear();
}

inline void intt(){
    Clear();
    read(n);read(m);
    for(int i=1;i<=n;i++){read(a[i]);b[i]=a[i];}
    sort(b+1,b+n+1);
    len=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++){
        int now=lower_bound(b+1,b+len+1,a[i])-b;
        rk[now]=a[i];a[i]=now;
    }
}

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(q);
    while(q--){
        intt();
        for(int i=1;i<=n;i++){
            // printf("i:%d\n",i);
            printf("%lld ",ds.BinaryFind(m-rk[a[i]]));
            ds.ChangeNode(a[i]);
        }
        puts("");
    }
}

1.6 2019 Multi-University,HDU Day 9 B

链接

首先因为没有两个点在一条直线上,所以这个题有一个性质是他的答案等于里面线段的交点个数加一。我们考虑和统计交点。我们考虑用扫描线。

我们用扫描线从左到右扫,对于一些方向向右的线段,我们在线段树上单点加,对于方向向上或向下的线段,我们直接在线段树上区间求和即可。方向向左的线段我们同样做,不过我们要从右向左扫。

一定要注意空间。

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define int long long
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

inline char Get(){
    char c=getchar();
    while(c!='L'&&c!='R'&&c!='U'&&c!='D') c=getchar();
    return c;
}

struct Line{
    int x,y;char dire;
};
Line a[N];

int t,n,m,k,b[N<<1],bn,len;
int L[N<<1],R[N<<1],U[N<<1],D[N<<1];

#define ls k<<1
#define rs k<<1|1
struct DS{
    ll p[N<<3];
    inline void PushUp(int k){
        p[k]=p[ls]+p[rs];
    }
    inline void TClear(int k,int l,int r){
        if(l==r){p[k]=0;return;}
        int mid=(l+r)>>1;
        TClear(ls,l,mid);TClear(rs,mid+1,r);PushUp(k);
    }
    inline void ANode(int k,int l,int r,int w,int x){
        if(l==r){p[k]+=x;return;}
        int mid=(l+r)>>1;
        if(w<=mid) ANode(ls,l,mid,w,x);
        else ANode(rs,mid+1,r,w,x);PushUp(k);
    }
    inline ll GSum(int k,int l,int r,int z,int y){
        if(l==z&&r==y) return p[k];
        int mid=(l+r)>>1;
        if(y<=mid) return GSum(ls,l,mid,z,y);
        else if(z>mid) return GSum(rs,mid+1,r,z,y);
        else return GSum(ls,l,mid,z,mid)+GSum(rs,mid+1,r,mid+1,y);
    }
    inline void TreeClear(){TClear(1,1,m);}
    inline void AddNode(int w,int x){ANode(1,1,m,w,x);}
    inline ll GetSum(int l,int r){return GSum(1,1,m,l,r);}
};
DS ds;

inline void Clear(){
    for(int i=1;i<=k;i++){
        L[a[i].x]=R[a[i].x]=U[a[i].x]=D[a[i].x]=0;
    }
    ds.TreeClear();
    // memset(ds.p,0,sizeof(ds.p));
    // memset(L,0,sizeof(L));
    // memset(R,0,sizeof(R));
    // memset(U,0,sizeof(U));
    // memset(D,0,sizeof(D));
    // memset(b,0,sizeof(b));
    // memset(a,0,sizeof(a));
    // n=m=k=bn=len=0;
}

inline void intt(){
    read(n);read(m);read(k);
    for(int i=1;i<=k;i++){
        read(a[i].x);read(a[i].y);a[i].dire=Get();
        b[i*2-1]=a[i].x;b[i*2]=a[i].y;
    }
    b[k*2+1]=n;b[k*2+2]=m;
    bn=k*2+2;sort(b+1,b+bn+1);
    len=unique(b+1,b+bn+1)-b-1;
    for(int i=1;i<=k;i++){
        a[i].x=lower_bound(b+1,b+len+1,a[i].x)-b;
        a[i].y=lower_bound(b+1,b+len+1,a[i].y)-b;
        if(a[i].dire=='L') L[a[i].x]=a[i].y;
        else if(a[i].dire=='R') R[a[i].x]=a[i].y;
        else if(a[i].dire=='U') U[a[i].x]=a[i].y;
        else if(a[i].dire=='D') D[a[i].x]=a[i].y;
    }
    n=lower_bound(b+1,b+len+1,n)-b;m=lower_bound(b+1,b+len+1,m)-b;
}

inline void Solve(){
    ll ans=0;
    // ds.TreeClear();
    for(int i=1;i<=n;i++){
        if(R[i]) ds.AddNode(R[i],1);
        if(D[i]) ans=ans+ds.GetSum(1,D[i]);
        if(U[i]) ans=ans+ds.GetSum(U[i],m); 
    }
    // printf("ans1:%lld\n",ans);
    ds.TreeClear();
    for(int i=n;i>=1;i--){
        if(L[i]) ds.AddNode(L[i],1);
        if(D[i]) ans=ans+ds.GetSum(1,D[i]);
        if(U[i]) ans=ans+ds.GetSum(U[i],m);
    }
    // printf("ans2:%lld\n",ans);
    printf("%lld\n",ans+1);Clear();
}

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    while(t--){
        intt();Solve();
    }
    return 0;
}

1.7 Codeforces 600 E

跑过去学了一下 dsu on tree ,因为实在不想写线段树合并。

这里简单介绍一下 dsu on tree,抽空在另外一篇博客上写一个完整的。

简单来说,dsu on tree 是一种静态链分治,用于统计子树上的信息,但不能指出修改。

比如给定一棵树,每一个节点上都有一个颜色,那么要求回答每一棵子树上的颜色种类个数。

我们考虑暴力怎么做,暴力就是枚举每一个节点,然后暴力统计这颗节点子树下颜色种类个数,用一个 \(cnt\) 来储存每一个颜色出现了多少次,然后暴力消去这棵子树对 \(cnt\) 数组产生的影响。复杂度显然 \(O(n^2)\)

dsu on tree 实际上就是把上面这个暴力进行了优化,我们先对其轻重链剖分来得到每一个节点的重儿子,然后我们先暴力统计这个节点的轻儿子,然后再统计重儿子,需要注意的是重儿子不用消除影响,这样的复杂度是 \(O(n\log n)\)

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

struct edge{
    int to,next;
    inline void intt(int to_,int ne_){
        to=to_;next=ne_;
    }
};
edge li[N<<1];

int head[N],tail;

inline void add(int from,int to){
    li[++tail].intt(to,head[from]);
    head[from]=tail;
}

int siz[N],deep[N],fa[N],son[N];

inline void dfs(int k,int fat){
    fa[k]=fat;deep[k]=deep[fat]+1;siz[k]=1;
    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(to==fat) continue;
        dfs(to,k);siz[k]+=siz[to];
        if(siz[son[k]]<siz[to]) son[k]=to;
    }
}

ll n,cnt[N],sum,c[N],maxx,ans[N];
bool vis[N];

inline void Add(int k){
    cnt[c[k]]++;
    if(maxx<cnt[c[k]]) maxx=cnt[c[k]],sum=c[k];
    else if(maxx==cnt[c[k]]) sum+=c[k];
    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(to==fa[k]||vis[to]) continue;
        Add(to);
    }
}

inline void Del(int k){
    cnt[c[k]]--;
    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(to==fa[k]||vis[to]) continue;
        Del(to);
    }
}

inline void dfs2(int k,bool op){
    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(to==fa[k]||to==son[k]) continue;
        dfs2(to,0);
    }
    if(son[k]){dfs2(son[k],1);vis[son[k]]=1;}

    Add(k);vis[son[k]]=0;
    ans[k]=sum;
    if(!op){Del(k);sum=0;maxx=0;}
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);
    for(int i=1;i<=n;i++) read(c[i]);
    for(int i=1;i<=n-1;i++){
        int from,to;read(from);read(to);
        add(from,to);add(to,from);
    }
    dfs(1,0);dfs2(1,0);
    for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
    return 0;
}//

1.8 POI2011 Tree Rotations

我们直接用线段树合并,容易发现一个节点的两颗子树互相交换只能使两个点分别属于两颗子树的逆序对改变,所以我们可以用权值线段树来表示一棵子树里出现的数,然后在合并的时候计数交换或这不交换两种情况下的逆序对各是多少。

我们来证明一下合并操作均摊下来是 \(O(n\log n)\),其中 \(n\)​ ​​是一开始的点数。即一开始有 \(n\) 棵只有一个叶子节点的权值线段树。

容易发现,对于每次调用合并函数,我们都有两种选择,要么停止递归,要么继续递归。容易发现,停止递归的次数不会超过继续递归次数的两倍。那么我们就可以看做整个合并的时间复杂度与继续递归的次数是同级的。如果我们要回收节点,那么回收节点的次数是不超过 \(O(n\log n)\)​ 这是因为我们一共就这些点,而在线段树合并中是不会有新的节点产生的。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Min(T a,T b){return a<b?a:b;}

ll sum1,sum2,ans;
int tot,n;

struct node{
    int sum,l,r;
};
node p[N*22];

inline void PushUp(int k){
    p[k].sum=p[p[k].l].sum+p[p[k].r].sum;
}

inline int NewNode(){++tot;p[tot].l=p[tot].r=0;return tot;}

inline int Merge(int a,int b,int l,int r){
    if(!a||!b) return a+b;
    if(l==r){p[a].sum+=p[b].sum;return a;}
    int mid=(l+r)>>1;
    sum1=sum1+1ll*p[p[b].r].sum*p[p[a].l].sum;
    sum2=sum2+1ll*p[p[a].r].sum*p[p[b].l].sum;
    p[a].l=Merge(p[a].l,p[b].l,l,mid);
    p[a].r=Merge(p[a].r,p[b].r,mid+1,r);
    PushUp(a);return a;
}

inline void CNode(int &k,int l,int r,int w,int x){
    if(!k) k=NewNode();
    if(l==r){p[k].sum+=x;return;}
    int mid=(l+r)>>1;
    if(w<=mid) CNode(p[k].l,l,mid,w,x);
    else CNode(p[k].r,mid+1,r,w,x);PushUp(k);
}

inline int dfs(){
    int val,now=0;read(val);
    if(val==0){
        int ls=dfs(),rs=dfs();
        sum1=sum2=0;
        now=Merge(ls,rs,1,n);
        ans=ans+Min(sum1,sum2);
    }
    else{
        CNode(now,1,n,val,1);
    }
    return now;
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);dfs();
    printf("%lld\n",ans);
    return 0;
}

1.9 CF1354D Multiset

可以发现这个就是一个平衡树水题,但是空间上卡平衡树,所以我们使用权值线段树。这就是个裸题。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int n,q;

#define ls k<<1
#define rs k<<1|1
struct DS{
    int p[N<<2],tot;
    inline int NewNode(){return ++tot;}
    inline void PushUp(int k){p[k]=p[ls]+p[rs];}
    inline void CNode(int k,int l,int r,int w,int x){
        if(l==r){p[k]+=x;return;}
        int mid=(l+r)>>1;
        if(w<=mid) CNode(ls,l,mid,w,x);
        else CNode(rs,mid+1,r,w,x);PushUp(k);
    }
    inline void ChangeNode(int w,int x){CNode(1,1,n,w,x);}
    inline int BFind(int k,int rank,int l,int r){
        if(l==r){return l;}int mid=(l+r)>>1;
        if(p[ls]>=rank) return BFind(ls,rank,l,mid);
        else return BFind(rs,rank-p[ls],mid+1,r);
    }
    inline int BinaryFind(int rank){
        if(p[1]<rank) return -1;return BFind(1,rank,1,n);
    }
    inline void Print(){
        if(p[1]==0) printf("0\n");
        else printf("%d\n",BinaryFind(1));
    }
};
DS ds;

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(q);
    for(int i=1;i<=n;i++){
        int x;read(x);ds.ChangeNode(x,1);
    }
    for(int i=1;i<=q;i++){
        int k;read(k);
        if(k<0){
            int val=ds.BinaryFind(-k);
            if(val!=-1) ds.ChangeNode(val,-1);
        }
        else{
            ds.ChangeNode(k,1);
        }
    }
    ds.Print();
    return 0;
}

1.10 CF1181D Irrigation

我们发现这个东西排序之后相当于一个灌水模型,全部灌满我们特判一下,特殊处理,没有灌满的我们可以进行二分,但是难办的是我们需要查找一个前缀中第 \(k\) 小的是谁,我直接写了一个可持久化权值平衡树,在上面二分过了这个题。同机房的人说可以直接用平衡树来做,需要把所有的询问离线下来从小到大做。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 500010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int m,n,q,sum[N];

struct rode{
    int water,id;
    inline bool operator < (const rode &b) const{
        if(water!=b.water) return water<b.water;
        else return id<b.id;
    }
};
rode a[N];

struct node{
    int sum,l,r;
};node p[N*25];

struct DS{
    int root[N],tot,siz;
    inline int NewNode(){return ++tot;}
    inline void PushUp(int k){p[k].sum=p[p[k].l].sum+p[p[k].r].sum;}
    inline void Build(int &k,int l,int r){
        if(!k) k=NewNode();if(l==r) return;
        int mid=(l+r)>>1;
        Build(p[k].l,l,mid);Build(p[k].r,mid+1,r);
    }
    inline void BuildTree(){Build(root[0],1,m);}
    inline int ANode(int k,int l,int r,int w,int x){
        int q=NewNode();p[q]=p[k];
        if(l==r){p[q].sum+=x;return q;}
        int mid=(l+r)>>1;
        if(w<=mid) p[q].l=ANode(p[k].l,l,mid,w,x);
        else p[q].r=ANode(p[k].r,mid+1,r,w,x);PushUp(q);
        return q;
    }
    inline void AddNode(int w,int x){
        siz++;root[siz]=ANode(root[siz-1],1,m,w,x);
    }
    inline int BFind(int k,int rank,int l,int r){
        if(l==r) return l;
        int mid=(l+r)>>1;
        if(p[p[k].l].sum>=rank) return BFind(p[k].l,rank,l,mid);
        else return BFind(p[k].r,rank-p[p[k].l].sum,mid+1,r);
    }
    inline int BinaryFind(int x,int rank){return BFind(root[x],rank,1,m);}
};
DS ds;

inline bool Check(int mid,ll now){
    ll res=1ll*mid*a[mid].water;
    return res-(ll)sum[mid]<now;
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(m);read(q);
    for(int i=1;i<=n;i++){
        int x;read(x);a[x].water++;
    }
    for(int i=1;i<=m;i++) a[i].id=i;
    sort(a+1,a+m+1);
    ds.BuildTree();
    for(int i=1;i<=m;i++)
        ds.AddNode(a[i].id,1);
    for(int i=1;i<=m;i++) sum[i]=sum[i-1]+a[i].water;
    ll all=1ll*m*a[m].water-sum[m];
    // printf("all:%lld\n",all);
    for(int i=1;i<=q;i++){
        ll now;read(now);
        now-=n;
        // printf("now:%lld\n",now);
        if(now>all){
            // printf("here\n");
            now-=all;now=now%m;
            if(now==0) now=m;
            printf("%lld\n",now);
        }
        else{
            int l=1,r=m;
            while(l<r){
                int mid=(l+r+1)>>1;
                if(Check(mid,now)) l=mid;
                else r=mid-1;
            }
            // printf("l:%d\n",l);
            ll shengxia=now-(1ll*l*a[l].water-(ll)sum[l]);
            // printf("shengxia:%lld\n",shengxia);
            shengxia=shengxia%l;if(shengxia==0) shengxia=l;
            ll ans=ds.BinaryFind(l,shengxia);
            printf("%lld\n",ans);
        }
    }
    return 0;
}
posted @ 2021-08-24 19:44  hyl天梦  阅读(60)  评论(0编辑  收藏  举报