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。

浙公网安备 33010602011771号