有趣的题目
1.Bertown roads
- 题意:无向联通图,给边定向,让图成为强连通图
- 思路:dfs树边+返祖边
- 证明: 首先存在割边就不行了,因为割边断开后分为至少两个部分。
然后根所连接的儿子的low都等于1,即根所到的每一个子树里都存在有点能回到根。
递归考虑下面子树每个子树的根也同理。直到叶子也能回到它的父亲之上。
考虑到上面这堆废话告诉我们一件事情:每个点能到达它父亲之上,因此所有点都能从返祖边一直跳到根,就得证。
2.嗅探器
- 题意:找到最小编号点,该点为A,B路径的必经点。
- 思路:脑补求点双联通分量的过程,A,B在dfs树路径的所有割点中最小的,把A当根就可以了。
- 代码:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int M=1e6+5;
int cnt,dfn[M],low[M],to[M],son,fa[M],step,nxt[M],head[M],tot;
bool mark[M];
void add_edge(int u,int v) {to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
void dfs(int u) {
step++;
dfn[u]=low[u]=step;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(!dfn[v]) {
fa[v]=u;
dfs(v);
if(low[v]>=dfn[u]) {
if(dfn[u]==1) {son++;}
else if(!mark[u]) {mark[u]=true;cnt++;}
}
low[u]=min(low[u],low[v]);
}
else {
if(fa[u]==v) continue;
low[u]=min(low[u],dfn[v]);
}
}
}
int main() {
int n,a,b,ans=0x3f3f3f3f;
scanf("%d",&n);
while(1) {
int u,v;
scanf("%d%d",&u,&v);
if(!u&&!v) break;
add_edge(u,v); add_edge(v,u);
}
scanf("%d%d",&a,&b);
dfs(a);
int to=b;
while(fa[to]!=a) {
if(dfn[fa[to]]<=low[to]) ans=min(ans,fa[to]);
to=fa[to];
}
if(ans==0x3f3f3f3f) printf("No solution");
else printf("%d",ans);
return 0;
}
3.道路封锁
这道题搞的挺久的~qaq
- 题目:任何两点间都会进行去来两次外交。问分别删除每个点会使外交减少的次数。
- 思路:数学柿子:注意要存两个size,size2,因为我的求法中一个点u->v,v的子树不应定受u影响,统计答案的部分式子是要用的这点性质的。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
ll ans[N],size[N],size2[N];
int n,m,dfn[N],low[N],Time,nxt[N],to[N],head[N],cut[N],ecnt=1;
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
void Tarjan(int u,int lst) {
dfn[u]=low[u]=++Time,size[u]=size2[u]=1;
int son=0;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(v==lst)continue;
if(!dfn[v]) {
son++;
Tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]) {
ans[u]+=1ll*(n-size[v])*size[v];
cut[u]=1;
size2[u]+=size[v];
}
size[u]+=size[v];
}
else {
low[u]=min(low[u],dfn[v]);
}
}
if(lst==-1&&son==1) cut[u]=0;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) {
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v),add_edge(v,u);
}
for(int i=1;i<=n;i++) if(!dfn[i])Tarjan(i,-1);
for(int i=1;i<=n;i++) {
if(!cut[i]) printf("%lld\n",1ll*(n-1)*2);
else printf("%lld\n",1ll*ans[i]+(n-1)+1ll*(n-size2[i])*size2[i]);
}
return 0;
}