2021.11.12考试总结[NOIP模拟83]

T1 树上排列

考场上一直以为要转换什么限制,结果就是哈希一下。。(不过确实没见过这个套路

找个与顺序无关的哈希,树剖维护一下就行了。(我写的平方加和好像是假的

\(code:\)

T1
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL; typedef long double LD;
    typedef unsigned long long ULL; typedef double DB;
    #define int LL
    #define freopen FL=freopen
    FILE *FL;
    const int Mxdt=100000;
    static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
    inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
    inline int read(){
        int t=0,f=0;char v=gc();
        while(v<'0'||v>'9')f|=(v=='-'),v=gc();
        while(v>='0'&&v<='9')t=(t<<3)+(t<<1)+v-48,v=gc();
        return f?-t:t;
    }
    void write(int x,char sp){
        int len=0;
        if(x<0) x=-x,putchar('-');
        do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
    }
    void ckmin(int& x,int y){ x=x<y?x:y; }
    void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=250010;
int t,n,q,op,u,v,a[NN],perm[NN];
int idx,head[NN];
bool vis[NN];
struct edge{ int to,nex; }e[NN<<1];
void add(int a,int b){
    e[++idx]=(edge){b,head[a]}; head[a]=idx;
    e[++idx]=(edge){a,head[b]}; head[b]=idx;
}

namespace Tree_Chain{
    int cnt,id[NN],fa[NN],dfn[NN],dep[NN],top[NN],siz[NN],son[NN];
    void dfs1(int s,int f){
        fa[s]=f; siz[s]=1; dep[s]=dep[f]+1;
        for(int v,i=head[s];i;i=e[i].nex) if((v=e[i].to)!=f){
            dfs1(v,s);
            siz[s]+=siz[v];
            if(siz[v]>siz[son[s]]) son[s]=v;
        }
    }
    void dfs2(int s,int t){
        dfn[s]=++cnt; top[s]=t; id[cnt]=s;
        if(!son[s]) return;
        dfs2(son[s],t);
        for(int v,i=head[s];i;i=e[i].nex)
            if((v=e[i].to)!=fa[s]&&e[i].to!=son[s]) dfs2(v,v);
    }
    int LCA(int x,int y){
        while(top[x]!=top[y])
            dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
        return dep[x]<dep[y]?x:y;
    }
    namespace Segment_Tree{
        #define ld rt<<1
        #define rd (rt<<1)|1
        int sm[NN<<2];
        void pushup(int rt){ sm[rt]=sm[ld]+sm[rd]; }
        void build(int rt,int l,int r){
            if(l==r) return sm[rt]=a[id[l]]*a[id[l]],void();
            int mid=l+r>>1;
            build(ld,l,mid); build(rd,mid+1,r);
            pushup(rt);
        }
        void modify(int pos,int val,int rt=1,int l=1,int r=n){
            if(l==r) return sm[rt]=val,void();
            int mid=l+r>>1;
            if(pos<=mid) modify(pos,val,ld,l,mid);
            else modify(pos,val,rd,mid+1,r);
            pushup(rt);
        }
        int query(int opl,int opr,int rt=1,int l=1,int r=n){
            if(l>=opl&&r<=opr) return sm[rt];
            int mid=l+r>>1,res=0;
            if(opl<=mid) res+=query(opl,opr,ld,l,mid);
            if(opr>mid) res+=query(opl,opr,rd,mid+1,r);
            return res;
        }
    } using namespace Segment_Tree;
    int ASK(int x,int y){
        int res=0;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]]) swap(x,y);
            res+=query(dfn[top[x]],dfn[x]);
            x=fa[top[x]];
        }
        if(dep[x]>dep[y]) swap(x,y);
        return res+query(dfn[x],dfn[y]);
    }
} using namespace Tree_Chain;

signed main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    t=read();
    for(int i=1;i<=250000;i++) perm[i]=perm[i-1]+i*i;
    while(t--){
        idx=cnt=0;
        memset(head,0,sizeof(head));
        memset(son,0,sizeof(son));
        n=read(); q=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int a,b,i=1;i<n;i++)
            a=read(),b=read(),add(a,b);
        dfs1(1,0); dfs2(1,1); build(1,1,n);
        while(q--){
            op=read(); u=read(); v=read();
            if(op==1){
                if(!u||!v){ puts("No"); continue; }
                int len=dep[u]+dep[v]-2*dep[LCA(u,v)]+1;
                if(ASK(u,v)==perm[len]) puts("Yes");
                else puts("No");
            } else modify(dfn[u],v*v);
        }
    }
    return 0;
}

T2 连任

可撤销并查集加线段树分治,但这俩都不会。。

并查集要撤销就不能路径压缩,只启发式合并。每次合并时把操作压进栈内,便于之后撤销。

在时间轴上建立线段树,把每条边存在的时段插进线段树内,之后在线段树上 \(DFS\) ,进入节点时将边的端点合并,回溯时撤销。

\(code:\)

T4
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    #define int LL
    typedef long long LL; typedef long double LD;
    typedef unsigned long long ULL; typedef double DB;
    typedef pair<int,int> PII;
    #define mpr make_pair
    #define fi first
    #define se second
    #define freopen FL=freopen
    FILE *FL;
    const int Mxdt=100000;
    static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
    inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
    inline int read(){
        int t=0,f=0;char v=gc();
        while(v<'0'||v>'9')f|=(v=='-'),v=gc();
        while(v>='0'&&v<='9')t=(t<<3)+(t<<1)+v-48,v=gc();
        return f?-t:t;
    }
    void write(int x,char sp){
        int len=0;
        if(x<0) x=-x,putchar('-');
        do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
    }
    void ckmin(int& x,int y){ x=x<y?x:y; }
    void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=100010,mod=1e9+7;
int n,m,u,v,op,tot,now,inv[NN],ans[NN];
struct node{ int u,v,l,r;}opr[NN];
map<int,int>tim[NN];

namespace DSU{
    int top,fa[NN],siz[NN];
    PII stk[NN];
    int getf(int x){ return fa[x]==x?x:getf(fa[x]); }
    void merge(PII s){
        int x=getf(s.fi),y=getf(s.se);
        if(x==y) return stk[++top]=mpr(-1,-1),void();
        now=now*inv[siz[x]]%mod*inv[siz[y]]%mod*(siz[x]+siz[y])%mod;
        if(siz[x]>siz[y]) swap(x,y);
        fa[x]=y; siz[y]+=siz[x];
        stk[++top]=mpr(x,y);
    }
    void delet(){
        int x=stk[top].fi,y=stk[top].se; --top;
        if(x==-1) return;
        now=now*inv[siz[y]]%mod*siz[x]%mod*(siz[y]-siz[x])%mod;
        fa[x]=x; siz[y]-=siz[x];
    }
} using namespace DSU;

namespace Devide_on_Segment_Tree{
    #define ld rt<<1
    #define rd (rt<<1)|1
    vector<PII>add[NN<<2];
    void modify(int opl,int opr,PII val,int rt=1,int l=1,int r=m){
        if(l>=opl&&r<=opr)
            return add[rt].push_back(val),void();
        int mid=l+r>>1;
        if(opl<=mid) modify(opl,opr,val,ld,l,mid);
        if(opr>mid) modify(opl,opr,val,rd,mid+1,r);
    }
    void solve(int rt,int l,int r){
        for(auto v:add[rt]) merge(v);
        if(l==r) ans[l]=now;
        else{
            int mid=l+r>>1;
            solve(ld,l,mid);
            solve(rd,mid+1,r);
        }
        for(auto v:add[rt]) delet();
    }
} using namespace Devide_on_Segment_Tree;

signed main(){
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    n=read(); m=read(); now=1;
    inv[1]=1;
    for(int i=2;i<=n;i++)
        inv[i]=mod-(mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    for(int i=1;i<=m;i++){
        op=read(); u=read(); v=read();
        if(op==1){
            tim[u][v]=tim[v][u]=++tot;
            opr[tot]=(node){u,v,i,0};
        } else opr[tim[u][v]].r=i-1;
    }
    for(int i=1;i<=tot;i++){
        if(!opr[i].r) opr[i].r=m;
        modify(opr[i].l,opr[i].r,mpr(opr[i].u,opr[i].v));
    }
    solve(1,1,m);
    for(int i=1;i<=m;i++) write(ans[i],'\n');
    return 0;
}

T3 排列

三维偏序竟能单log求。。

实际上考场上也就差这一步了。考虑现有三个排列 \(o,p,q\) ,要求它们的三维偏序对数。

我们对 \((o,p),(p,q),(o,q)\) 分别做二维偏序,对于一对下标 \(i,j\) ,会有三个偏序 \((o_i,o_j),(p_i,p_j),(q_i,q_j)\) ,但偏序只有大于小于两种关系,因此这一对下标要么恰好存在两个数字满足偏序关系,要么满足三维偏序。前者在三个二维偏序中会被算 \(1\) 遍,后者会被算 \(3\) 遍。容斥一下,令三个二维偏序对数和为 \(M\) ,则总三维偏序对数 \(N\)

\[N=\frac{M-\frac{n(n-1)}{2}}{2} \]

对于 \(q\neq-1\) 的情况,答案可以用这种方法算出。考虑 \(q=-1\) 的情况,算出这个 \(q\) 取值大于任意数 \(x\) 的概率 \(prob_x\) ,分别考虑它之前不为 \(-1\) 的数,它之后不为 \(-1\) 的数和除它之外的 \(-1\) 产生贡献的期望,加和即可。具体实现就是二维偏序。

\(code:\)

T3
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL; typedef long double LD;
    typedef unsigned long long ULL; typedef double DB;
    #define freopen FL=freopen
    FILE *FL;
    const int Mxdt=100000;
    static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
    inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
    inline int read(){
        int t=0,f=0;char v=gc();
        while(v<'0'||v>'9')f|=(v=='-'),v=gc();
        while(v>='0'&&v<='9')t=(t<<3)+(t<<1)+v-48,v=gc();
        return f?-t:t;
    }
    void write(int x,char sp){
        int len=0;
        if(x<0) x=-x,putchar('-');
        do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
    }
    void ckmin(int& x,int y){ x=x<y?x:y; }
    void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=1000010,mod=998244353,inv2=499122177;
int n,ans,p[NN],q[NN],prob[NN];
bool vis[NN];
vector<int>pos,num;
void add(int& a,int b){
    a+=b;
    if(a<0) a+=mod;
    if(a>=mod) a-=mod;
}
int qpow(int a,int b,int res=1){
    for(;b;b>>=1,a=1ll*a*a%mod)
        if(b&1) res=1ll*res*a%mod;
    return res;
}

namespace BIT{
    int c[NN];
    void insert(int pos,int val){
        while(pos<=n)
            add(c[pos],val), pos+=pos&-pos;
    }
    int query(int pos,int res=0){
        while(pos)
            add(res,c[pos]), pos-=pos&-pos;
        return res;
    }
} using namespace BIT;

struct node{
    int x,y;
    bool operator<(const node& a){
        return x<a.x;
    }
}nd[NN];
int cnt;
void solve(){
    for(int i=1;i<=n;i++) if(q[i]>0){
        insert(p[i],1);
        add(ans,query(p[i]-1));
    }
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;i++) if(q[i]>0){
        insert(q[i],1);
        add(ans,query(q[i]-1));
    }
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;i++) if(q[i]>0)
        nd[++cnt]=(node){p[i],q[i]};
    sort(nd+1,nd+cnt+1);
    for(int i=1;i<=cnt;i++){
        insert(nd[i].y,1);
        add(ans,query(nd[i].y-1));
    }
    ans=1ll*(ans-1ll*cnt*(cnt-1)/2%mod+mod)*inv2%mod;
}

signed main(){
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++) p[i]=read();
    for(int i=1;i<=n;i++){
        q[i]=read();
        if(q[i]>0) vis[q[i]]=1;
    }
    for(int i=1;i<=n;i++)
        if(!vis[i]) num.push_back(i);
    solve();
    if(num.empty()) return write(ans,'\n'),0;
    int tmp=qpow(num.size(),mod-2);
    for(int i=0;i<num.size();i++)
        prob[num[i]-1]=tmp;
    for(int i=n;i;i--) add(prob[i],prob[i+1]);
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;i++)
        if(q[i]>0) insert(p[i],prob[q[i]]);
        else add(ans,query(p[i]-1));
    memset(c,0,sizeof(c));
    for(int i=n;i;i--)
        if(q[i]>0) insert(p[i],mod+1-prob[q[i]]);
        else add(ans,query(n)-query(p[i]));
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;i++) if(q[i]<0){
        insert(p[i],inv2);
        add(ans,query(p[i]-1));
    }
    write(ans,'\n');
    return 0;
}

T4 追逐

image

属实给我调吐了

\(code:\)

T4
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL; typedef long double LD;
    typedef unsigned long long ULL; typedef double DB;
    #define freopen FL=freopen
    FILE *FL;
    const int Mxdt=100000;
    static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
    inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
    inline int read(){
        int t=0,f=0;char v=gc();
        while(v<'0'||v>'9')f|=(v=='-'),v=gc();
        while(v>='0'&&v<='9')t=(t<<3)+(t<<1)+v-48,v=gc();
        return f?-t:t;
    }
    void write(int x,char sp){
        int len=0;
        if(x<0) x=-x,putchar('-');
        do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
    }
    void ckmin(int& x,int y){ x=x<y?x:y; }
    void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=1000010;
int n,t,s,res,sum,fa[NN];
int f[NN];
bool on[NN];
vector<int>e[NN];
void add(int a,int b){
    e[a].push_back(b);
    e[b].push_back(a);
}

void dfs(int u,int ff){
    int cnt=0,mx1=0,mx2=0; fa[u]=ff;
    for(int v:e[u]) if(v!=ff){
        ++cnt; dfs(v,u);
        int tmp=f[v];
        if(tmp>mx1) swap(mx1,tmp);
        if(tmp>mx2) swap(mx2,tmp);
    }
    f[u]=mx2+cnt;
}
void init(){
    int u=s,pre=0;
    while(u!=t){
        for(int v:e[u]) sum+=(v!=fa[u]&&v!=pre);
        pre=u; u=fa[u];
    }
}

bool check(int mid){
    int u=s,pre=0,all=sum,del=0,now=1;
    while(u!=t){
        int tmp=0,pmt=0;
        for(int v:e[u]) if(v!=pre&&v!=fa[u]){
            if(f[v]+all+del>mid) ++tmp;
            ++pmt;
        }
        all-=pmt; del+=tmp;
        if(del>now||del>mid) return 0;
        pre=u; u=fa[u]; ++now;
    }
    return 1;
}
void search(){
    int l=0,r=1000;
    while(l<=r){
        int mid=l+r>>1;
        if(check(mid)) r=mid-1,res=mid;
        else l=mid+1;
    }
}
signed main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    n=read(); t=read(); s=read();
    for(int a,b,i=1;i<n;i++)
        a=read(),b=read(),add(a,b);
    dfs(t,0); init(); search();
    write(res,'\n');
    return 0;
}
posted @ 2021-11-13 07:05  keen_z  阅读(46)  评论(0)    收藏  举报