CSP 模拟 8

T1 基础的生成函数练习题(gf)

目标要么是最大数,要么是最大数加一,简单分讨一下做完了,(也没人会搜这个的题解吧)

T2 简单的拉格朗日反演练习题(lagrange)

做法一:
显然答案具有合并性质,上 Kruskal 重构树,lca 表示的是这两个节点联通的最小 \(k\),然后拿 st 表就可以处理区间,lca 可以 dfs 序 \(\mathcal{O}(1)\) 求,时间复杂度 \(\mathcal{O}(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e5+10;
int n,m,q,st[20][N],fa[N],w[N],dfn[N],dfc,ska[20][N];
std::vector<int> g[N];
struct Edge{
    int u,v,w;
    bool operator <(const Edge&a)const {return w<a.w;}
}e[N<<2];
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
inline int get(int u,int v){return dfn[u]>dfn[v]?v:u;}
inline void dfs(int u,int fa){
    st[0][dfn[u]=++dfc]=fa;
    for(int v:g[u])dfs(v,u);
}
inline int LCA(int u,int v){
    if(u==v)return u;
    if((u=dfn[u])>(v=dfn[v]))std::swap(u,v);
    int d=std::__lg(v-u++);
    return get(st[d][u],st[d][v-(1<<d)+1]);
}
inline int ask(int l,int r){
    l++;
    if(l>r)return 0;
    int d=std::__lg(r-l+1);
    return std::max(ska[d][l],ska[d][r-(1<<d)+1]);
}
inline void kruskal(){
    dfc=0;
    for(int i=1;i<=2*n;++i)fa[i]=i;
    std::stable_sort(e+1,e+m+1);
    int cnt=0,_n=n;
    for(int i=1;i<=m&&cnt<=2*n-2;++i){
        int u=find(e[i].u),v=find(e[i].v);
        if(u==v)continue;
        ++_n;g[_n].emplace_back(u);g[_n].emplace_back(v);
        cnt+=2;
        fa[u]=fa[v]=_n;
        w[_n]=e[i].w;
    }
    dfs(_n,0);n*=2;
    for(int i=1;i<=std::__lg(n);++i)
        for(int j=1;j+(1<<i)-1<=n;++j)
            st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);
    n/=2;
    for(int i=2;i<=n;++i)ska[0][i]=w[LCA(i,i-1)];
    for(int i=1;i<=std::__lg(n);++i)
        for(int j=1;j+(1<<i)-1<=n;++j)
            ska[i][j]=std::max(ska[i-1][j],ska[i-1][j+(1<<i-1)]);
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    int T=read();
    while(T--){
        n=read(),m=read(),q=read();
        for(int i=1;i<=m;++i)e[i].u=read(),e[i].v=read(),e[i].w=i;
        kruskal();
        for(int i=1;i<=q;++i){
            int l=read(),r=read();
            std::cout<<ask(l,r)<<' ';
        }
        std::cout<<'\n';
        for(int i=n;i<=n*2;++i)g[i].clear();
    }
}

做法二:
很多 Kruskal 重构树的题都可以上主席树增加一个 log,显然答案具有单调性,考虑二分,用并查集维护连通块,然后用主席树维护每个点每个时刻的所属连通块编号,并查集需要启发式合并,时间空间复杂度 \(\mathcal{O}(n\log^2n)\),主席树每次都要多点修改,空间巨大,常数巨大,OJ 过不了,CF 勉强过。(byd 我赛时写的这个,写题解的时候竟然忘了咋做。)

点击查看代码
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e5+10;
int n,m,q,fa[N],root[N],cnt;
std::vector<int>son[N];
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
struct TREE{
    int ls,rs,ans;
}t[N*76];
inline void update(int p){
    if(t[t[p].ls].ans==t[t[p].rs].ans){
        t[p].ans=t[t[p].ls].ans;
    }else{
        t[p].ans=-1;
    }
    return;
}
inline void build(int p,int l,int r){
    if(l==r){t[p].ans=l;return;}
    int mid=l+r>>1;
    build(t[p].ls=++cnt,l,mid);
    build(t[p].rs=++cnt,mid+1,r);
    update(p);
}
inline int query(int p,int l,int r,int x,int y){
    if(l>=x&&r<=y){return t[p].ans;}
    int mid=l+r>>1,res=0;
    if(x<=mid){
        int zc=query(t[p].ls,l,mid,x,y);
        if(!res){res=zc;}
        else if(res!=zc){res=-1;}
    }
    if(y>mid){
        int zc=query(t[p].rs,mid+1,r,x,y);
        if(!res){res=zc;}
        else if(res!=zc)res=-1;
    }
    return res;
}
inline void insert(int p,int l,int r,int last,int pos,int x){
    if(l==r){t[p].ans=x;return;}
    int mid=l+r>>1;
    t[p]=t[last];
    if(pos<=mid){
        insert(t[p].ls=++cnt,l,mid,t[last].ls,pos,x);
    }
    else{
        insert(t[p].rs=++cnt,mid+1,r,t[last].rs,pos,x);
    }
    update(p);
}
inline void add(int x,int y,int k){
    x=find(x),y=find(y);
    if(x==y){root[k]=root[k-1];return;}
    if(son[x].size()>son[y].size())std::swap(x,y);
    fa[x]=y;
    int last=k-1;
    for(int it:son[x]){
        son[y].push_back(it);
        fa[it]=find(it);
        cnt++;
        int zc=cnt;
        insert(zc,1,n,root[last],it,fa[it]);
        root[k]=zc;
        last=k;
    }son[x].clear();
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    int T=read();
    while(T--){
        n=read(),m=read(),q=read();
        cnt=0;
        for(int i=1;i<=n;++i)son[i].clear(),fa[i]=i,son[i].push_back(i);
        root[0]=++cnt;
        build(root[0],1,n);
        for(int i=1;i<=m;++i){
            int x=read(),y=read();
            add(x,y,i);
        }
        for(int i=1;i<=q;++i){
            int x=read(),y=read();
            int l=0,r=m,ans=0;
            while(l<=r){
                int mid=l+r>>1;
                if(query(root[mid],1,n,x,y)!=-1)r=mid-1,ans=mid;
                else l=mid+1;
            }
            std::cout<<ans<<' ';
        }
        std::cout<<'\n';
    }
}

做法三:
颜旭的做法,答案具有合并性质,考虑不用 Kruskal 重构树维护出第 \(i\) 个点与第 \(i+1\) 个点联通的 \(k\),还是上并查集,依次加边,启发式合并每次查看 \(i\) 是否与 \(i+1\) 联通,查询上 st 表,时间复杂度 \(\mathcal{O}(n)\),好写,常数优秀。

点击查看代码
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e5+10;
int n,m,q,st[22][N],fa[N];
std::vector<int> son[N];
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
inline int ask(int l,int r){
    r--;
    if(l>r)return 0;
    int d=std::__lg(r-l+1);
    return std::max(st[d][l],st[d][r-(1<<d)+1]);
}
inline void add(int u,int v,int k){
    u=find(u),v=find(v);
    if(u==v)return;
    if(son[u].size()>son[v].size())std::swap(u,v);
    fa[u]=v;
    for(int x:son[u]){
        if(find(x)==find(x-1))st[0][x-1]=std::min(st[0][x-1],k);
        if(find(x)==find(x+1))st[0][x]=std::min(st[0][x],k);
        son[v].emplace_back(x);
    }son[u].clear();
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    int T=read();
    while(T--){
        n=read(),m=read(),q=read();
        for(int i=1;i<=n;++i)st[0][i]=INT_MAX;
        for(int i=1;i<=n;++i)fa[i]=i,son[i].emplace_back(i);
        for(int i=1;i<=m;++i){
            int u=read(),v=read();add(u,v,i);
        }
        for(int i=1;i<=std::__lg(n);++i)
            for(int j=1;j+(1<<i)-1<=n;++j)
                st[i][j]=std::max(st[i-1][j],st[i-1][j+(1<<i-1)]);
        for(int i=1;i<=q;++i){
            int l=read(),r=read();
            std::cout<<ask(l,r)<<' ';
        }std::cout<<'\n';
    }
}

T3 容易的多元拉格朗日反演练习题(multi)

究极困难博弈论,颜旭赛时猜结论过了,膜拜猜结论之神,黑题,一点不会。

T4 朴素的抽象代数题(algebra)

唐氏 joke3579 整了一堆无意义内容硬控大家,转化完题意后就是找循环节,比较简单的 T4。

总结

这场 T2 没做出来还是比较唐氏,T4 没打上暴力。期望 225pts。

posted @ 2024-08-18 21:32  Ishar-zdl  阅读(39)  评论(1)    收藏  举报