P3367
#include <bits/stdc++.h>
using namespace std;
int n;
int pre[10001];
int root(int x){
return pre[x]=(pre[x]==x?x:root(pre[x]));
}
void merge(int x,int y){
x=root(x),y=root(y);
pre[x]=y;
}
int main(){
int m;
cin>>n>>m;
for(int i=1;i<=n;i++) pre[i]=i;
while(m--){
int z,x,y;
cin>>z>>x>>y;
switch(z){
case 1:
merge(x,y);
break;
case 2:
if(root(x)==root(y)) printf("Y\n");
else printf("N\n");
}
}
}
思路:并查集模板。
P8604
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1001;
int n,m;
int st,ed;
int dn,dfn[N],low[N],cnt;
vector<int> e[N];
vector<int> path;
set<int> buc;
bool reach = 0;
void dfs(int id,bool p){
if(id == ed) reach = 1;
dfn[id] = low[id] = ++dn;
int i=0;
for(int it : e[id]){
if(!dfn[it]){
dfs(it,i);
if(!p && reach){
path.push_back(id);
}
if(reach) i=1;
low[id] = min(low[id],low[it]);
if(low[it] >= dfn[id] && id != st && id!= ed) buc.insert(id);
}
else low[id] = min(low[id],dfn[it]);
}
}
int main(){
cin>>n>>m;
for(int i = 1;i <= m;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v),e[v].push_back(u);
}
cin>>st>>ed;
path.push_back(ed);
dfs(st,0);
if(!reach){
cout<<"-1"<<endl;
}
else{
for(auto b: buc){
auto it = find(path.begin(),path.end(),b);
if(it != path.end() && low[ed] >= dfn[b] && low[*(it-1)] >= dfn[b]) cnt++;
}
cout<<cnt;
}
}
思路:用Tarjan算法找出所有的割点,再对每一个割点判断其是否将给出的两个点分割。
P1330
#include <bits/stdc++.h>
using namespace std;
#define read(a) {char c;c=getchar();while(!(c>='0'&&c<='9')){c=getchar();}while(c>='0'&&c<='9'){a=a*10+(c^48);c=getchar();}}
constexpr int maxN = 100100, maxM = 1000100;
int n, m;
set<int> point;
typedef struct{
int u, v, nxt;
}edge;
edge e[maxM];
int head[maxN];
inline void addedge(int u, int v){
static int cnt = 0;
e[++cnt].v = v;
e[cnt].u = u;
e[cnt].nxt = head[u];
head[u] = cnt;
}
bool vis[maxN],hx[maxN];
struct pos{
int pt, st;
};
void bfs(){
int n = 0, cnt = 0, rst = 0;
pos cur, nxt;
cur.pt = *(point.begin()), cur.st = 1;
queue<pos> q;
label:
q.push(cur);
vis[cur.pt] = 1;
n = 0, cnt = 0;
while(!q.empty()){
cur = q.front();
q.pop();
hx[cur.pt] = cur.st%2;
cnt += hx[cur.pt], n++;
nxt.st = cur.st+1;
for(int i = head[cur.pt];i;i = e[i].nxt){
if(!vis[e[i].v]){
nxt.pt = e[i].v;
vis[e[i].v] = 1;
q.push(nxt);
}
}
}
rst += min(cnt, n-cnt);
for(auto i: point){
if(!vis[i]){
cur.pt = i,cur.st = 1;
goto label;
}
}
for(int i = 1;i <= m;i++){
if(hx[e[i].u] == hx[e[i].v]){
cout<<"Impossible";
return;
}
}
cout<<rst;
}
int main(){
cin>>n>>m;
for(int i = 0;i < m;i++){
int x = 0, y = 0;
read(x);read(y);
point.insert(x);
point.insert(y);
addedge(x, y);
addedge(y, x);
}
bfs();
}
思路:bfs,将河蟹全部放在奇数步的位置或者全部放在偶数步的位置,然后检查每一条边,如果边的两端都有或都没有河蟹则impossible。
P3916
#include <bits/stdc++.h>
using namespace std;
#define read(a) {char c;c=getchar();while(!(c>='0'&&c<='9')){c=getchar();}while(c>='0'&&c<='9'){a=a*10+(c^48);c=getchar();}}
constexpr int maxN = 100100, maxM = 100100;
int n,m;
typedef struct{
int u, v, nxt;
}edge;
edge e[maxM];
int head[maxN], rst[maxN];
bool vis[maxN];
inline void addedge(int u, int v){
static int cnt = 0;
e[++cnt].v = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
void bfs(int st){
int cur = st, nxt;
queue<int> q;
q.push(cur);
vis[cur] = 1;
while(!q.empty()){
cur = q.front();
q.pop();
rst[cur] = max(rst[cur], st);
for(int i = head[cur];i;i = e[i].nxt){
if(!vis[e[i].v]){
nxt = e[i].v;
vis[nxt] = 1;
q.push(nxt);
}
}
}
}
int main(){
read(n);read(m);
for(int i = 0;i < m;i++){
int x = 0, y = 0;
read(x);read(y);
addedge(y, x);
}
for(int j = n;j > 0;j--){
bfs(j);
}
for(int i = 1;i <= n;i++){
printf("%d ", rst[i]);
}
}
思路:反向建边,按编号从大到小顺序作为起点进行搜索,并且每次搜索不需要清空vis数组,访问一个点时直接将其答案更新为当前起点编号。
P1119
#include<iostream>
#include<cstdio>
#define N 205
using namespace std;
int n,m;
int a[N];
int f[N][N];
inline void floyd(int k){
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(f[i][j]>f[i][k]+f[j][k])
f[i][j]=f[j][i]=f[i][k]+f[j][k];
return;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++) scanf("%d",a+i);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
f[i][j]=1e9;
}
for(int i=0;i<n;i++)f[i][i]=0;
int s1,s2,s3;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&s1,&s2,&s3);
f[s1][s2]=f[s2][s1]=s3;
}
int q;
cin>>q;
int now=0;
for(int i=1;i<=q;i++){
scanf("%d%d%d",&s1,&s2,&s3);
while(a[now]<=s3&&now<n){
updata(now);
now++;
}
if(a[s1]>s3||a[s2]>s3)cout<<-1<<endl;
else {
if(f[s1][s2]==1e9)cout<<-1<<endl;
else cout<<f[s1][s2]<<endl;
}
}
return 0;
}
思路:每过一天就用修好的村庄更新所有村庄的最短路径,直到所询问的天数。