noip模拟83

noip模拟83 solutions

真难,真难,真难,难死我了!!!

所以我啥时候可以\(AK\)一次啊???

今天好多自己应该会的但是没有做出来。。。

T1 树上排列

这就是一个可重集哈希大板子题,可是我竟然忘记了!!??

考场上一直想着要用线段树判重,害,真脑残!。。

所以我莫名其妙的开发了一个序列判重的办法

直接\(set\)记录前驱,线段树维护区间最大前驱是不是在区间内,如果在就有重复的

序列判重
set<int> st[N];
int pre[N];
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    int mx[N],las[N];
    void pushup(int x){
        mx[x]=max(mx[ls],mx[rs]);
        las[x]=max(las[ls],las[rs]);
        return ;
    }
    void build(int x,int l,int r){
        if(l==r){
            mx[x]=a[l];las[x]=pre[l];
            return ;
        }
        int mid=l+r>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(x);return ;
    }
    void ins(int x,int l,int r,int pos){
        if(l==r){
            mx[x]=a[l];las[x]=pre[l];
            return ;
        }
        int mid=l+r>>1;
        if(pos<=mid)ins(ls,l,mid,pos);
        else ins(rs,mid+1,r,pos);
        pushup(x);return ;
    }
    int query_mx(int x,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr)return mx[x];
        int mid=l+r>>1,ret=0;
        if(ql<=mid)ret=max(ret,query_mx(ls,l,mid,ql,qr));
        if(qr>mid)ret=max(ret,query_mx(rs,mid+1,r,ql,qr));
        pushup(x);return ret;;
    }
    int query_las(int x,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr)return las[x];
        int mid=l+r>>1,ret=0;
        if(ql<=mid)ret=max(ret,query_las(ls,l,mid,ql,qr));
        if(qr>mid)ret=max(ret,query_las(rs,mid+1,r,ql,qr));
        pushup(x);return ret;;
    }
    #undef ls
    #undef rs
}xds;

void spj(){
    //cout<<"SB"<<endl;
    fo(i,1,n){
        if(st[a[i]].begin()!=st[a[i]].end())pre[i]=*st[a[i]].rbegin();
        st[a[i]].insert(i);
    }
    xds.build(1,1,n);
    fo(i,1,q){
        int tp,x,y;
        tp=read();x=read();y=read();
        if(tp==1){
            if(x>y)swap(x,y);
            int mx=xds.query_mx(1,1,n,x,y);
            int las=xds.query_las(1,1,n,x,y);
            if(mx==y-x+1&&las<x)printf("Yes\n");
            else printf("No\n");
        }
        else {
            set<int>::iterator it=st[a[x]].find(x);
            if(next(it)!=st[a[x]].end()){
                if(it!=st[a[x]].begin())pre[*next(it)]=*prev(it);
                else pre[*next(it)]=0;
                xds.ins(1,1,n,*next(it));
            }
            st[a[x]].erase(x);
            a[x]=y;st[a[x]].insert(x);pre[x]=0;
            it=st[a[x]].find(x);
            if(next(it)!=st[a[x]].end()){
                pre[*next(it)]=x;
                xds.ins(1,1,n,*next(it));
            }
            if(it!=st[a[x]].begin())pre[x]=*prev(it);
            //cout<<pre[3]<<" "<<pre[4]<<" "<<a[3]<<endl;
            xds.ins(1,1,n,x);
            //cout<<xds.query_las(1,1,n,4,4)<<endl;
        }
    }
}

正解非常简单,直接树剖+线段树,判断路径上的加和是否等于一个排列的加和就行了

如果怕被卡,可以用两个模数

战神指导:可以直接把每一个数都换掉!

AC_code


#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
    int s=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
const int N=3e5+5;
const ull bas=131;
int T,n,q,a[N];
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
    to[++rp]=y;
    nxt[rp]=head[x];
    head[x]=rp;
}
int ans;ull an[N];
int dfn[N],cnt,idf[N];
int fa[N],dep[N];
int son[N],siz[N],top[N];
ull ba[N];
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    ull hs[N*4];
    void pushup(int x){
        hs[x]=hs[ls]+hs[rs];
        return ;
    }
    void build(int x,int l,int r){
        if(l==r){hs[x]=ba[a[idf[l]]];return ;}
        int mid=l+r>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(x);return ;
    }
    void ins(int x,int l,int r,int pos){
        if(l==r){hs[x]=ba[a[idf[l]]];return ;}
        int mid=l+r>>1;
        if(pos<=mid)ins(ls,l,mid,pos);
        else ins(rs,mid+1,r,pos);
        pushup(x);return ;
    }
    ull query(int x,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr)return hs[x];
        int mid=l+r>>1;ull ret=0;
        if(ql<=mid)ret+=query(ls,l,mid,ql,qr);
        if(qr>mid)ret+=query(rs,mid+1,r,ql,qr);
        pushup(x);return ret;
    }
    #undef ls
    #undef rs
}xds;
void dfs_fi(int x,int f){
    siz[x]=1;son[x]=0;
    dep[x]=dep[f]+1;fa[x]=f;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f)continue;
        dfs_fi(y,x);
        siz[x]+=siz[y];
        if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
    }
}
void dfs_se(int x,int f){
    top[x]=f;dfn[x]=++cnt;idf[cnt]=x;
    if(son[x])dfs_se(son[x],f);
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==fa[x]||y==son[x])continue;
        dfs_se(y,y);
    }
}
bool LCA(int x,int y){
    ull ret=0;int len=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        ret+=xds.query(1,1,n,dfn[top[x]],dfn[x]);
        len+=dep[x]-dep[fa[top[x]]];x=fa[top[x]];
    }
    if(dep[x]<dep[y])swap(x,y);
    ret+=xds.query(1,1,n,dfn[y],dfn[x]);
    len+=dep[x]-dep[y]+1;
    return ret==an[len];
}

signed main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    T=read();
    while(T--){
        n=read();q=read();rp=0;cnt=0;
        ba[0]=1;fo(i,1,n)ba[i]=ba[i-1]*bas,an[i]=an[i-1]+ba[i];
        //cout<<an[1]<<" "<<an[2]<<" "<<an[5]<<endl;
        fo(i,1,n)head[i]=0;
        fo(i,1,n)a[i]=read();
        fo(i,1,n-1){
            int x,y;x=read();y=read();
            add_edg(x,y);add_edg(y,x);
        }
        dfs_fi(1,0);dfs_se(1,1);
        xds.build(1,1,n);
        fo(i,1,q){
            int tp,x,y;
            tp=read();x=read();y=read();
            if(tp==1){
                if(!x||!y){printf("No\n");continue;}
                if(LCA(x,y))printf("Yes\n");
                else printf("No\n");
            }
            else {
                a[x]=y;
                xds.ins(1,1,n,dfn[x]);
            }
        }
    }
    return 0;
}

T2 连任

可撤销并查集??!

不会,好像还有一个可持久化并查集。。

不会,都不会

所以我就去学习了一下,就是用一个栈记录合并信息,按秩合并,不能路径压缩

到时候直接把所有的合并全都倒出来就好了

还用到线段树分分治,说白了就是在线段树上\(dfs\)

不然的话无法撤销。。

AC_code


#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
    int s=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
const int N=1e5+5;
const int mod=1e9+7;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int n,m,ans[N],tot,res;
map<pair<int,int>,int> mp;
pair<int,int> ys[N];
int bg[N],ed[N];
struct DSU{
    int fa[N],siz[N];
    pair<int,int> sta[N];
    int top;
    void init(){
        fo(i,1,n)fa[i]=i,siz[i]=1;
        top=0;
    }
    int find(int x){return fa[x]==x?x:find(fa[x]);}
    int merge(int x,int y){
        int fx=find(x),fy=find(y);
        if(fx==fy)return 0;
        if(siz[fx]>siz[fy])swap(fx,fy);
        fa[fx]=fy;
        res=res*ksm(siz[fx],mod-2)%mod*ksm(siz[fy],mod-2)%mod;
        siz[fy]+=siz[fx];
        res=res*siz[fy]%mod;
        sta[++top]=make_pair(fx,fy);
        return 1;
    }
    void cancel(){
        fa[sta[top].first]=sta[top].first;
        res=res*ksm(siz[sta[top].second],mod-2)%mod;
        siz[sta[top].second]-=siz[sta[top].first];
        res=res*siz[sta[top].second]%mod*siz[sta[top].first]%mod;
        top--;return ;
    }
}dsu;
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    vector<int> vec[N*4];
    void ins(int x,int l,int r,int ql,int qr,int v){
        if(ql<=l&&r<=qr)return vec[x].push_back(v),void();
        int mid=l+r>>1;
        if(ql<=mid)ins(ls,l,mid,ql,qr,v);
        if(qr>mid)ins(rs,mid+1,r,ql,qr,v);
        return ;
    }
    void dfs(int x,int l,int r){
        int num=0;
        for(int i:vec[x])num+=dsu.merge(ys[i].first,ys[i].second);
        if(l==r){
            ans[l]=res;
            while(num--)dsu.cancel();
            return ;
        }
        int mid=l+r>>1;
        dfs(ls,l,mid);
        dfs(rs,mid+1,r);
        while(num--)dsu.cancel();
        return ;
    }
    #undef ls
    #undef rs
}xds;
signed main(){
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    n=read();m=read();
    fo(i,1,m){
        int tp,u,v;
        tp=read();u=read();v=read();
        if(tp==1){
            mp[make_pair(u,v)]=++tot;
            ys[tot]=make_pair(u,v);
            bg[tot]=i;ed[tot]=m;
        }
        else ed[mp[make_pair(u,v)]]=i-1;
    }
    dsu.init();
    fo(i,1,tot)xds.ins(1,1,m,bg[i],ed[i],i);
    res=1;xds.dfs(1,1,m);
    fo(i,1,m)printf("%lld\n",ans[i]);
    return 0;
}

T3 排列

这我考场上\(CDQ\)打假了..

原因是我有一个可以用\(lower_bound\)求的东西,我用双指针的时候没有卡边界。。

于是发现一个高级东西,三维偏序可以\(\mathcal{O(nlogn)}\)求出来

容斥呗!就这样啦

AC_code


#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
    int s=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
const int N=1e6+5;
const int mod=998244353;
int n,ans;
struct node{int p,q,id;}a[N];
bool com1(node a,node b){return a.id<b.id;}
bool com2(node a,node b){return a.p<b.p;}
bool vis[N];
int pos[N],avl[N],cnt;
int inv[N],g[N];
struct BIT{
    int tr[N];
    void ins(int x,int v){
        for(int i=x;i<=n;i+=(i&-i))tr[i]+=v;
    }
    int query(int x){
        int ret=0;
        for(int i=x;i;i-=(i&-i))ret+=tr[i];
        return ret;
    }
}bit;
signed main(){
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    // cerr<<(sizeof(dp)+sizeof(inv)*20>>20)<<endl;
    n=read();
    fo(i,1,n)a[i].p=read(),a[i].id=i;
    fo(i,1,n){
        a[i].q=read();
        if(a[i].q!=-1)vis[a[i].q]=true;
    }
    inv[0]=inv[1]=1;
    fo(i,2,n)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    fo(i,1,n)if(!vis[i])avl[++cnt]=i;
    int now=1;
    fo(i,1,n){
        while(avl[now]<i&&now<=cnt)now++;
        pos[i]=now;
    }
    fo(i,1,n){
        ans=(ans+bit.query(a[i].p))%mod;
        bit.ins(a[i].p,1);
    }
    fo(i,1,n)bit.ins(a[i].p,-1);
    int sum=0,big=0;
    fo(i,1,n){
        if(a[i].q==-1){
            ans=(ans+sum*inv[2])%mod;
            ans=(ans+big)%mod;
            sum++;
        }
        else {
            ans=(ans+bit.query(a[i].q))%mod;
            ans=(ans+(pos[a[i].q]-1)*inv[cnt]%mod*sum)%mod;
            big=(big+(cnt-pos[a[i].q]+1)*inv[cnt])%mod;
            bit.ins(a[i].q,1);
        }
    }
    fo(i,1,n)if(a[i].q!=-1)bit.ins(a[i].q,-1);
    sort(a+1,a+n+1,com2);
    sum=0;big=0;
    fo(i,1,n){
        if(a[i].q==-1){
            ans=(ans+sum*inv[2])%mod;
            ans=(ans+big)%mod;
            sum++;
        }
        else {
            ans=(ans+bit.query(a[i].q))%mod;
            ans=(ans+(pos[a[i].q]-1)*inv[cnt]%mod*sum)%mod;
            big=(big+(cnt-pos[a[i].q]+1)*inv[cnt])%mod;
            bit.ins(a[i].q,1);
        }
    }
    fo(i,1,n)if(a[i].q!=-1)bit.ins(a[i].q,-1);
    ans=(ans-n*(n-1)%mod*inv[2]%mod+mod)*inv[2]%mod;
    printf("%lld",ans);
    return 0;
}

T4 追逐

这个题就玄学,我调着调着样例过了,交上去就切了

我还没看懂题解呢,害,人品太好了。。。

所以最优策略问题我就没有做出来过,每次都是等着题解告诉我策略是啥。。

这个最优策略就是挤到一个角落里然后清出一条路来,直接走过去就行了

这个用二分实现。。

AC_code


#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
    int s=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
const int N=1e6+5;
int n,t,s;
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
    to[++rp]=y;
    nxt[rp]=head[x];
    head[x]=rp;
}
int dp[N],dep[N],fa[N],siz[N];
bool ifst[N];
vector<int> e[N];
bool com(int x,int y){return dp[x]>dp[y];}
void dfs_dp(int x,int f){
    if(x==s)ifst[x]=true;
    dep[x]=dep[f]+1;fa[x]=f;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f)continue;
        dfs_dp(y,x);
        ifst[x]|=ifst[y];
    }
    if(ifst[x])return ;
    int mx=0,mi=0,sum=0;
    for(int i=head[x];i;i=nxt[i]){
        if(to[i]==f)continue;
        if(dp[to[i]]>=dp[mx])mi=mx,mx=to[i];
        else if(dp[to[i]]>=dp[mi])mi=to[i];
        sum++;
    }
    if(!mi&&!mx)dp[x]=0;
    else if(!mi)dp[x]=1;
    else {
        dp[x]=dp[mi]+sum;
    }
}
void dfs_siz(int x,int f){
    siz[x]+=e[x].size();
    for(int i=head[x];i;i=nxt[i]){
        if(to[i]==f)continue;
        siz[to[i]]+=siz[x];
        dfs_siz(to[i],x);
    }
}
bool check(int x){
    int now=s,num=0,stp=1,tmp,mx=0;
    while(now!=t){
        tmp=0;
        for(int i:e[now]){
            if(dp[i]+siz[now]+num>x)tmp++;
            else mx=max(mx,dp[i]);
        }
        num+=tmp;
        if(num>stp)return false;
        now=fa[now];stp++;
    }
    return mx+num<=x;
}
signed main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    n=read();t=read();s=read();
    fo(i,1,n-1){
        int x=read(),y=read();
        add_edg(x,y);add_edg(y,x);
    }
    dfs_dp(t,0);
    fo(x,1,n){
        if(x==t)continue;
        for(int i=head[x];i;i=nxt[i]){
            if(ifst[to[i]])continue;
            e[x].push_back(to[i]);
        }
        sort(e[x].begin(),e[x].end(),com);
    }
    dfs_siz(t,0);
    // cout<<siz[2]<<endl;
    // cout<<dp[3]<<" "<<dp[4]<<endl;
    int l=0,r=n,mid;
    while(l<r){
        mid=l+r>>1;
        if(check(mid))r=mid;
        else l=mid+1;
    }
    printf("%d",l);
    return 0;
}
上一篇:[HDU6643] Ridiculous Netizens


下一篇:题解——P4556 [Vani有约会]雨天的尾巴