2023.8.11
不背图论板子要反省一下自己了。
A
求
\(1\le L\le R\le 10^6\).
先容斥。假定 \(n\le m\).
如果 \(x=1\) 或 \(y=1\) 那么 \((x,y)\) 一定被统计了,要减去 \(\displaystyle\lfloor\frac{n}{d}\rfloor+\lfloor\frac{m}{d}\rfloor-1\).
前面一小块就是 \(nm\),后面是 \(\displaystyle\sum_{p=1}^{n}\mu(p)\lfloor\frac{n}{p}\rfloor\lfloor\frac{m}{p}\rfloor\).
时间复杂度 \(O(n)\).
当然不用莫反估计也能做。
B
一张无向连通图,问
-
割边后两点的连通性
-
割点后两点的连通性
\(n\le 10^5\),\(m\le 5\times 10^5\),\(q\le 3\times 10^5\).
luogu \(\rm ML=62.5MiB\).
tarjan 的话第一问更简单,但是考场上过载了。
先来看一下 part2.
一个普通的想法是点双之后判断删点在不在两点的路径上,这个直接拍到圆方树上做就好了。
具体来说直接倍增把三者的 lca 弄出来判一判即可。当然也可以写个树剖。
再看下 part1.
只需要考虑边为桥的情况。那么这两个点应该与同一个方点相邻。
这样问题就又变成了 part2,把这个桥对应的方点拉出来做一遍即可。
时间复杂度 \(O(n\log n)\),空间复杂度 \(O(n\sim n\log n)\).
求桥时圆方树的算法里做 low[u]=min(low[u],dfn[v])
是一定不能爬父边的,虽然它不会对圆方树的结构产生影响。
#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define pb push_back
#define mp make_pair
#define mit map<pair<int,int>,int>::iterator
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
bool gets(){
char ch=getchar();
while(ch!='A'&&ch!='C')ch=getchar();
return ch=='C';
}
int n,m,q,cnt;
vector<int>e[N],T[N<<1];
int dfn[N],low[N],tim;
int st[N],tp;
map<pair<int,int>,int>bri;
void tarjan(int u,int fa){
low[u]=dfn[u]=++tim;
st[++tp]=u;
for(int v:e[u]){
if(!dfn[v]){
tarjan(v,u),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
cnt++;
if(low[v]>dfn[u])
bri[minmax(u,v)]=cnt;
for(int x=0;x!=v;tp--){
x=st[tp];
T[cnt].pb(x),T[x].pb(cnt);
}
T[cnt].pb(u),T[u].pb(cnt);
}
}
else if(v!=fa)low[u]=min(low[u],dfn[v]);
}
}
int fa[N<<1],dep[N<<1],siz[N<<1],son[N<<1];
int top[N<<1],dfc;
void dfs1(int u){
siz[u]=1;
for(int v:T[u]){
if(dep[v])continue;
fa[v]=u,dep[v]=dep[u]+1;
dfs1(v),siz[u]+=siz[v];
if(siz[son[u]]<siz[v])son[u]=v;
}
}
void dfs2(int u,int t){
top[u]=t;
if(son[u])dfs2(son[u],t);
for(int v:T[u])
if(fa[v]==u&&v!=son[u])dfs2(v,v);
}
bool crs(int x,int y,int z){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
if(top[x]==top[z]&&dep[z]<=dep[x])return true;
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
if(top[x]==top[z]&&dep[z]>=dep[x]&&dep[z]<=dep[y])return true;
return false;
}
int main(){
n=read(),m=read();
cnt=n;
for(int i=1,u,v;i<=m;i++){
u=read(),v=read();
e[u].pb(v),e[v].pb(u);
}
tarjan(1,0);
dep[1]=1,dfs1(1),dfs2(1,1);
q=read();
for(int opt,a,b,c,d;q;q--){
opt=read(),a=read(),b=read(),c=read();
if(opt==1){
d=read();
mit it=bri.find(minmax(c,d));
if(it==bri.end())puts("yes");
else puts(crs(a,b,it->second)?"no":"yes");
}
else puts(crs(a,b,c)?"no":"yes");
}
return 0;
}
C
求 DAG 的一颗生成树,且根为 \(n\)(保证图上 \(in_n=0\)),试让所有非根节点的儿子数的最大值最小,并给出 \(0\sim n-1\) 的字典序最小的父亲序列。DAG 不连通输出 \(-1\).
\(n\le 50\).
不考虑字典序,先最小化儿子数的最大值,容易二分一个 \(k\),建图直接跑最大流,\(maxflow=n\) 说明 \(k\) 合法。
再去想字典序的问题,假设已经确定 \(fa_{0\sim n-1}\),枚举 \(fa_i\).
假设选择 \(fa_i\),再建一遍图跑最大流,为 \(n\) 说明 \(fa_i\) 合法,继续枚举 \(i+1\).
要跑 \(n^2\) 次网络流,不太会分析复杂度,应该是 \(O(n^4\sim n^5)\).
D
不会。