NOIP图论Summary
NOIP2013 货车运输
算法:最大生成树,倍增LCA
思路:保证图联通以后,发现小于最大生成树最小边的边都肯定不会走
所以直接建最大生成树,然后每次询问x,y的路径最小值,直接在求lca的时候一起维护就可以。
代码:
#include<bits/stdc++.h>
using namespace std;
const int INF=1e9+7;
int n,m;
int fa[10100];
struct node{
int x,y,z;
}R[500100];
struct xxx{
int nxt,v,val;
}e[500100*2];
int cnt,ans;
int anc[10010][30];
int wei[10010][30];
int vis[10010];
int dep[10010];
int head[10010];
void add(int u,int v,int val){
e[++cnt].nxt=head[u];
e[cnt].v=v;
e[cnt].val=val;
head[u]=cnt;
}
bool cmp(node x,node y){
return x.z>y.z;
}
int getf(int p){
return fa[p]==p?p:fa[p]=getf(fa[p]);
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void dfs(int u,int p,int d){
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==p) continue;
if(vis[v]==1) continue;
wei[v][0]=e[i].val;
anc[v][0]=u;
dep[v]=d+1;
dfs(v,u,d+1);
}
}
void init(){
for(int i=1;i<=n;i++){
if(!vis[i]){
dep[i]=1;
dfs(i,0,1);
anc[i][0]=0;
wei[i][0]=INF;
}
}
for(int j=1;j<=20;j++){
for(int i=1;i<=n;i++){
anc[i][j]=anc[anc[i][j-1]][j-1];
wei[i][j]=min(wei[i][j-1],wei[anc[i][j-1]][j-1]);
}
}
}
void swim(int &x,int h){
for(int i=0;h>0;i++){
if(h&1){
ans=min(ans,wei[x][i]);
x=anc[x][i];
}
h>>=1;
}
}
int lca(int u,int v){
if(getf(u)!=getf(v)) return -1;
ans=INF;
if(dep[u]<dep[v]) swap(u,v);
swim(u,dep[u]-dep[v]);
if(u==v) return ans;
for(int i=20;i>=0;i--){
if(anc[u][i]!=anc[v][i]){
ans=min(ans,min(wei[u][i],wei[v][i]));
u=anc[u][i];
v=anc[v][i];
}
}
ans=min(ans,min(wei[u][0],wei[v][0]));
return ans;
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
R[i].x=read();
R[i].y=read();
R[i].z=read();
}
for(int i=1;i<=n;i++){
fa[i]=i;
}
sort(R+1,R+m+1,cmp);
for(int i=1;i<=m;i++){
int f1=getf(R[i].x);
int f2=getf(R[i].y);
if(f1!=f2){
fa[f1]=f2;
add(R[i].x,R[i].y,R[i].z);
add(R[i].y,R[i].x,R[i].z);
}
}
init();
int q;
q=read();
while(q--){
int x,y;
x=read();
y=read();
printf("%d\n",lca(x,y));
}
return 0;
}
NOIP2009 最优贸易
算法:缩点+DAG dp
思路:发现一个强连通分量中的点都可以互相到达,直接缩点,维护缩点后的每个点最大和最小的价格
然后就可以在DAG上dp,dp[i]表示能走到i点的所有贸易方案中获利最大值,再记f[i]表示能走到i的最小价格
直接转移即可
答案为dp[belong[n]];
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=500000;
inline int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
int n,m;
int a[maxn];
int dfn[maxn];
int low[maxn];
int cnt1,top,dep,sum,cnt2;
int stack[maxn];
int head1[maxn];
int belong[maxn];
int head2[maxn];
int maxv[maxn];
int minv[maxn];
int vis[maxn];
int in[maxn];
int topo[maxn];
int dp[maxn];
int f[maxn];
struct node{
int nxt,v;
}e1[maxn*2],e2[maxn*2];
void add1(int u,int v){
e1[++cnt1].nxt=head1[u];
e1[cnt1].v=v;
head1[u]=cnt1;
}
void add2(int u,int v){
e2[++cnt2].nxt=head2[u];
e2[cnt2].v=v;
head2[u]=cnt2;
}
void tarjan(int u){
dfn[u]=low[u]=++dep;
vis[u]=1;
stack[++top]=u;
for(int i=head1[u];i;i=e1[i].nxt){
int v=e1[i].v;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[v],low[u]);
}
else{
if(vis[v]){
low[u]=min(dfn[v],low[u]);
}
}
}
if(dfn[u]==low[u]){
belong[u]=++sum;
vis[u]=0;
minv[sum]=1e9+7;
while(stack[top]!=u){
belong[stack[top]]=sum;
vis[stack[top]]=0;
maxv[sum]=max(maxv[sum],a[stack[top]]);
minv[sum]=min(minv[sum],a[stack[top]]);
top--;
}
maxv[sum]=max(maxv[sum],a[u]);
minv[sum]=min(minv[sum],a[u]);
top--;
}
}
int main(){
// freopen("work.in","r",stdin);
// freopen("work.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
int x,y,z;
for(int i=1;i<=m;i++){
x=read(),y=read(),z=read();
if(z==1){
add1(x,y);
}
else{
add1(x,y);
add1(y,x);
}
}
for(int i=1;i<=n;i++){
if(!dfn[i]){
tarjan(i);
}
}
for(int i=1;i<=n;i++){
for(int j=head1[i];j;j=e1[j].nxt){
int v=e1[j].v;
if(belong[i]!=belong[v]){
add2(belong[i],belong[v]);
in[belong[v]]++;
}
}
}
int tail=0;
for(int i=1;i<=sum;i++){
if(in[i]==0){
topo[++tail]=i;
}
}
for(int i=1;i<=tail;i++){
int now=topo[i];
for(int j=head2[now];j;j=e2[j].nxt){
int v=e2[j].v;
in[v]--;
if(in[v]==0){
topo[++tail]=v;
}
}
}
f[topo[1]]=minv[topo[1]];
for(int i=1;i<=tail;i++){
int u=topo[i];
for(int j=head2[u];j;j=e2[j].nxt){
int v=e2[j].v;
f[v]=min(minv[v],f[u]);
dp[v]=max(max(dp[v],dp[u]),max(maxv[v]-minv[v],maxv[v]-f[v]));
}
}
cout<<dp[belong[n]]<<endl;
return 0;
}
NOIP2018 旅行
思路:
关于树,直接dfs,每次先搜叶子点里最小的点,O(n)
关于基环树,先类似于tarjan的方法找到环,记录下环
发现一定有一条环上的边不会走到
枚举一条环上边,删掉
再跑和树一样的dfs
找到最小的方案,O(n^2)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=10000;
inline int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
int n,m,cnt,top,dep,stp,cntl,flag,back;
int head[maxn];
struct node{
int v,nxt;
}e[maxn*2];
void add(int u,int v){
e[++cnt].nxt=head[u];
e[cnt].v=v;
head[u]=cnt;
}
int dfn[maxn];
int lop[maxn];
int faa[maxn];
int ans[maxn];
void dfs1(int u,int fa){
ans[++top]=u;
int vis[100];
memset(vis,0,sizeof(vis));
int cnt2=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==fa) continue;
vis[++cnt2]=v;
}
sort(vis+1,vis+cnt2+1);
for(int i=1;i<=cnt2;i++){
dfs1(vis[i],u);
}
return;
}
void find_loop(int u,int fa){
dfn[u]=++dep;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==fa) continue;
if(dfn[v]){
if(dfn[u]>dfn[v]) stp=v;
lop[++cntl]=u;
for(;v!=stp;v=faa[v]) lop[++cntl]=v;
}
else{
faa[v]=u;
find_loop(v,u);
}
}
return;
}
void dfs2(int u,int fa,int bu,int bv){
int vis[100];
if(back) return;
top++;
if(u<ans[top]) flag=1;
if(u>ans[top]&&flag==0) {
back=1;return;
}
if(flag) ans[top]=u;
memset(vis,0,sizeof(vis));
int cnt2=0;
int ban=0;
if(u==bu) ban=bv;
if(u==bv) ban=bu;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==fa||v==ban) continue;
vis[++cnt2]=v;
}
sort(vis+1,vis+cnt2+1);
for(int i=1;i<=cnt2;i++){
dfs2(vis[i],u,bu,bv);
}
return;
}
int main(){
n=read(),m=read();
int u,v;
for(int i=1;i<=m;i++){
u=read(),v=read();
add(u,v);
add(v,u);
}
if(n-1==m){
dfs1(1,1);
for(int i=1;i<=n;i++){
cout<<ans[i]<<' ';
}
cout<<endl;
return 0;
}
if(n==m){
find_loop(1,1);
memset(ans,0x3f,sizeof(ans));
for(int i=1;i<cntl;i++){
flag=0;
top=0;
back=0;
dfs2(1,1,lop[i],lop[i+1]);
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<' ';
}
cout<<endl;
return 0;
}
return 0;
}