1.CF427C Checkposts
题意:建立一些站点,如果有一个检查站在i路口,保护j的条件是:i==j或者警察巡逻车可以从i走到j,并且能回到i。求最小花费和用最少点的方案数
显然是强连通分量
在跑tarjan的过程中记录每个强连通分量中cost的最小值,在输出时遍历累乘即可
要开longlong,wa了两发
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=3e5+5;
constexpr int mod=1e9+7;
int n,m;
int cost[maxn],dfn[maxn],low[maxn],z[maxn],tot,k,vis[maxn];
int t,scc[maxn],cnt[maxn],top,res[maxn];
vector<int>e[maxn];
vector<int>jl[maxn];
void tarjan(int u){
dfn[u]=low[u]=++tot;
z[++top]=u;//入栈
vis[u]=1;
for(auto v:e[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
t++;//连通分量的标号
do{
scc[z[top]]=t; //属于这个连通分量
cnt[t]++; //记录这个环中有多少个点
res[t]=min(res[t],cost[z[top]]);
jl[t].push_back(z[top]);
vis[z[top]]=0;
top--; //出栈
}while(u!=z[top+1]);
}
}
signed main(){
cin>>n;
memset(res,0x3f,sizeof res);
for(int i=1;i<=n;i++){
cin>>cost[i];
}
cin>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
int ans=0,w=1;
for(int i=1;i<=t;i++){
ans+=res[i];
int ww=0;
for(auto v:jl[i]){
if(cost[v]==res[i])ww++;
}
w=((w*ww)%mod);
// printf("cnt = %d\n",cnt[i]);
}
printf("%lld %lld",ans,w);
return 0;
}
2.CF1406C Link Cut Centroids
题意:给定一棵节点数为 n 的树 , 删一条边然后加上一条边 , 使得该树的重心唯一 。(删掉的边和加上的边可以是同一条)
重心最多只能有两个
如果只有一个重心,输出唯一重心上的任意一条边两次即可
如果有两个重心,把b重心的上任意一条非跟a重心相连的边连到a重心上即可
#include<bits/stdc++.h>
using namespace std;
vector<int>e[200005];
int n,m,t,maxn1,maxn2,zx1,zx2;
int d[200005];
void dfs(int s,int f){
d[s]=1;
int res=0;
for(auto v:e[s]){
if(v==f)continue;
dfs(v,s);
d[s]+=d[v];
res=max(res,d[v]);
}
res=max(res,n-d[s]);
if(res<maxn1){
maxn1=res;
zx1=s;
}
if(res==maxn1&&s!=zx1){
maxn2=maxn1;
zx2=s;
}
}
int main(){
cin>>t;
while(t--){
cin>>n;
maxn1=0x3f3f3f3f,maxn2=0x3f3f3f3f+1;
memset(d,0,sizeof d);
for(int i=1;i<=n;i++){
e[i].clear();
}
for(int i=1;i<=n-1;i++){
int x,y;
cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1,0);
int f=0;
if(maxn1!=maxn2){
for(auto v:e[zx1]){
printf("%d %d\n",zx1,v);
printf("%d %d\n",zx1,v);
f=1;
break;
}
if(f)continue;
}
else if(maxn1==maxn2){
for(auto v:e[zx2]){
if(v!=zx1){
printf("%d %d\n",zx2,v);
printf("%d %d\n",v,zx1);
f=1;
break;
}
}
}
}
return 0;
}
3.CF1209D Cow and Snacks
n 种花,k 个客人,每个人喜欢两种编号不同的花。但是每种花在花店里只有一束。
客人按顺序进入花店,会买走所有她喜欢且仍在店铺里的花。如果一个客人买不到任何一束花,那么她就会十分沮丧导致变成肥宅。现在你可以自己安排这 n 个人的顺序,使得肥宅的数量最小。
并查集,
#include<bits/stdc++.h>
using namespace std;
vector<int>e;
int n,m;
const int maxn=2e5+5;
int f[maxn],vis[maxn];
int find(int x){
if(f[x]==x)return x;
else return f[x]=find(f[x]);
}
inline void hb(int x,int y){
int fx=find(x);
int fy=find(y);
f[fx]=fy;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
hb(x,y);
}
int cnt=0;
for(int i=1;i<=n;i++){
if(f[i]==i)cnt++;
}
cout<<m-n+cnt;
}
4.CF489D Unbearable Controversy of Being
题意:在一个有向图中,求类似于上图的菱形,即4个点 a , b , c , d a,b,c,d a,b,c,d 同时存在 ( a , b ) , ( a , d ) , ( b , c ) , ( d , c ) (a,b),(a,d),(b,c),(d,c) (a,b),(a,d),(b,c),(d,c)四条边组成的菱形的个数
思路:遍历求出每个点距离为2的路径数,再用组合数 n选2
可以表示成 a ( a − 1 ) 2 \dfrac {a(a-1)}2 2a(a−1)
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=3005,inf=0x3f3f3f3f;
constexpr int dx[]={1,-1,0,0};
constexpr int dy[]={0,0,1,-1};
int n,m,t,k;
vector<int>e[maxn];
int cnt[maxn][maxn];
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
}
for(int i=1;i<=n;i++){
for(auto u:e[i]){
for(auto v:e[u]){
if(i==v)continue;
cnt[i][v]++;
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
ans+=(cnt[i][j]-1)*cnt[i][j]/2;
}
}
cout<<ans;
return 0;
}
5.CF1176E Cover it!
题意:给你一张 n n n个点 m m m条边的无向连通图(没有边权)。保证图中没有重边和自环
你的任务是选择至多 ⌊ n 2 ⌋ \lfloor\frac{n}{2}\rfloor ⌊2n⌋个点使得对于任意一个没有被选择的点都和至少一个选中的点相邻(换言之,通过一条边连接)
保证答案存在,如果有多个答案,输出任意一个
你将要回答多个独立的询问
思路:二分图染色
题目保证有解并且图连通,不用判断无解情况
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
constexpr int dx[]={1,-1,0,0};
constexpr int dy[]={0,0,1,-1};
int n,m,t,k;
vector<int>e[maxn];
int cnt,color[maxn];
inline void init(){
cnt=0;
for(int i=1;i<=n;i++){
e[i].clear();
color[i]=-1;
}
}
void dfs(int u){
color[u]=0;
for(auto v:e[u]){
if(color[v]!=-1)continue;
dfs(v);
if(!color[v])color[u]=1;
}
}
signed main(){
cin>>t;
while(t--){
cin>>n>>m;
init();
int mm=0,nb=0;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1);
for(int i=1;i<=n;i++){
cnt+=color[i];
}
cout<<cnt<<endl;
for(int i=1;i<=n;i++){
if(color[i]){
cout<<i<<" ";
}
}
cout<<endl;
}
return 0;
}
6.CF1144F Graph Without Long Directed Paths
题意:给你一个无向图,把所有边标记方向,并使整张图中没有距离 >=2 的路径,问你是否存在并输出方案。
第一行输出“YES”或“NO”,若存在,第二行按照读入顺序输出连边方向
思路:二分图染色
既然距离不能达到2,那就白点向黑点连,判断有解即可
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
constexpr int dx[]={1,-1,0,0};
constexpr int dy[]={0,0,1,-1};
int n,m,t,k;
vector<int>e[maxn];
int cnt,color[maxn],vis[maxn];
int a[maxn],b[maxn];
void dfs(int u){
vis[u]=1;
for(auto v:e[u]){
if(!vis[v]){
color[v]=color[u]^1;
dfs(v);
}
else if(color[v]==color[u]){
cout<<"NO";
exit(0);
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
a[i]=u;
b[i]=v;
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;i++){
if(!vis[i])dfs(i);
}
cout<<"YES"<<endl;
for(int i=1;i<=m;i++){
cout<<color[a[i]];
}
return 0;
}
7.CF1093D Beautiful Graph
题意:给你一个 n n n 个点 m m m 条边的无向图。
你需要给每个点一个点权,使得每条边连接的两个点点权奇偶不同。点权的值域为 { 1 , 2 , 3 } \{1,2,3\} {1,2,3} 。
请求出方案数对 998244353 998244353 998244353 取模的结果。
二分图染色,用上简单的组合,开longlong,处处求mod,一个地方不注意就炸哭了
#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int maxn=3e5+5,inf=0x3f3f3f3f,mod=998244353;
int n,m,t;
vector<int>e[maxn];
int ans1=0,ans2=0;
int cnt,color[maxn],vis[maxn];
int flag=0;
int Pow(int x, int k){ int ans=1;for(;k;k>>=1,x=(int)x*x%mod) if(k&1) ans=(int)ans*x%mod;return ans;}
void dfs(int u,int tag){
if(flag)return;
if(color[u])ans1++;
else ans2++;
vis[u]=tag;
for(auto v:e[u]){
if(!vis[v]){
color[v]=color[u]^1;
dfs(v,tag);
}
else if(color[v]==color[u]){
flag=1;
return;
}
}
}
signed main(){
cin>>t;
while(t--){
cin>>n>>m;
for(int i=1;i<=n;i++){
e[i].clear();
vis[i]=0;
color[i]=0;
}
flag=0;
cnt=0;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
int ans=1;
for(int i=1;i<=n;i++){
if(!vis[i]){
++cnt;
ans1=0,ans2=0;
dfs(i,cnt);
ans=(int)ans*(Pow(2,ans1)%mod+Pow(2,ans2)%mod)%mod;
}
}
if(flag){
cout<<"0"<<endl;
continue;
}
cout<<ans<<endl;
}
return 0;
}
8.CF1027D Mouse Hunt
伯兰州立大学的医学部刚刚结束了招生活动。和以往一样,约80%的申请人都是女生并且她们中的大多数人将在未来4年(真希望如此)住在大学宿舍里。
宿舍楼里有 n n n个房间和一只老鼠!女孩们决定在一些房间里设置捕鼠器来除掉这只可怕的怪物。在 i i i号房间设置陷阱要花费 c i c_i ci伯兰币。房间编号从 1 1 1到 n n n。
要知道老鼠不是一直原地不动的,它不停地跑来跑去。如果 t t t秒时它在 i i i号房间,那么它将在 t + 1 t+1 t+1秒时跑到 a i a_i ai号房间,但这期间不会跑到别的任何房间里( i = a i i=a_i i=ai表示老鼠没有离开原来的房间)。时间从 0 0 0秒开始,一旦老鼠窜到了有捕鼠器的房间里,这只老鼠就会被抓住。
如果女孩们知道老鼠一开始在哪里不就很容易吗?不幸的是,情况不是这样,老鼠在第 0 0 0秒时可能会在从 1 1 1到 n n n的任何一个房间内。
那么女孩们最少要花多少钱设置捕鼠器,才能保证老鼠无论从哪个房间开始流窜最终都会被抓到?
思路:因为有n个点n条边,所以一定会存在环,每个点只有一条出边,所以这个环一定是在链的末尾,先用拓扑排序把环外的点消去,再在森林里找到各个环内cost最小的点,相加即可
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,cnt;
int du[maxn],c[maxn],vis[maxn],ans[maxn];
vector<int>e[maxn];
void topo(){
queue<int>q;
for(int i=1;i<=n;i++){
if(du[i]==0){
q.push(i);
}
}
while(!q.empty()){
int u=q.front();
q.pop();
for(auto v:e[u]){
du[v]--;
if(du[v]==0)q.push(v);
}
}
}
void dfs(int u,int tag){
if(vis[u]||!du[u])return;
vis[u]=1;
ans[tag]=min(ans[tag],c[u]);
for(auto v:e[u]){
dfs(v,tag);
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>c[i];
}
for(int i=1;i<=n;i++){
int v;
cin>>v;
du[v]++;
e[i].push_back(v);
}
topo();
memset(ans,inf,sizeof ans);
for(int i=1;i<=n;i++){
if(!vis[i]&&du[i])dfs(i,++cnt);
}
int res=0;
for(int i=1;i<=cnt;i++){
res+=ans[i];
}
cout<<res;
}
9.CF1320B Navigation System
给出 2 ≤ n ≤ 2 ⋅ 1 0 5 2\le n\le2\cdot 10^5 2≤n≤2⋅105 个节点, 2 ≤ m ≤ 2 ⋅ 1 0 5 2\le m\le2\cdot 10^5 2≤m≤2⋅105 条边的有向图,路径 p 1 , ⋯ , p k p_1,\cdots,p_k p1,⋯,pk,路径中没有重复元素,边 ( p i , p i + 1 ) (p_i,p_{i+1}) (pi,pi+1) 总是存在。
定义 s = p 1 , t = p k s=p_1,t=p_k s=p1,t=pk 。
有一个导航系统,若当前在节点 u u u,会构造一条从 u u u 到 t t t 的最短路径(这种路径可能不止一条,但导航系统只会选其中一条),设导航系统规划的下一个节点为 w w w,实际行走的下一个节点为 v v v 。
- w = v w=v w=v 不会触发重构。
- w ≠ v w\neq v w=v 会触发重构。
实际行走路线为 p p p,求可能的最少重构次数和最多重构次数。
思路:若当前点在最短路上,该点连边的点中,有其他点也为最短路上的点,可以选择重构,最大重构++
若当前点不在最短路上,因为必须重构,所以最大最小都++
建一个反图跑bfs,求出每一点到终点的最短路
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,k,cnt;
int dis[maxn],a[maxn],vis[maxn],s,t;
vector<int>e[maxn],r[maxn];
void bfs(){
dis[s]=0;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(auto v:r[u]){
if(!dis[v]){
dis[v]=dis[u]+1;
q.push(v);
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
r[v].push_back(u);
}
cin>>k;
for(int i=1;i<=k;i++){
cin>>a[i];
}
s=a[k],t=a[1];
bfs();
int ans1=0,ans2=0;
for(int i=1;i<k;i++){
if(dis[a[i]]==dis[a[i+1]]+1){
for(auto v:e[a[i]]){
if(v!=a[i+1]&&dis[v]==dis[a[i+1]]){
ans2++;
// cout<<"!"<<endl;
break;
}
}
}
else{
ans1++,ans2++;
}
}
cout<<ans1-1<<" "<<ans2-1;
}
10.CF698B Fix a Tree
题意:给出n个点的父亲,(若父亲为自己则为根节点)求出最少改掉多少个点的父亲可以使其成为一颗有根树
思路:用并查集判断树or环的个数,分两种情况:
若森林中有符合条件的树,选一个树根作为总根,将其他树or环的代表点连到根上即可输出,cnt-1
若森林中只有环,选一个环的代表点作为根,将其他的环的代表点连到根上即可,输出cnt(因为作为根的环把父节点改成了自己)
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,k,cnt,root;
int f[maxn],a[maxn],vis[maxn];
vector<int>e[maxn];
int find(int x){
if(f[x]==x)return x;
else return f[x]=find(f[x]);
}
inline void hb(int x,int y){
int fx=find(x);
int fy=find(y);
f[fx]=fy;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=1;i<=n;i++){
cin>>a[i];
hb(i,a[i]);
}
for(int i=1;i<=n;i++){
if(f[i]==i&&a[i]==i){
root=i;
}
}
if(root){
for(int i=1;i<=n;i++){
if(f[i]!=i)continue;
cnt++;
if(a[i]==i&&i!=root){
a[i]=root;
}
else a[i]=root;
}
cout<<cnt-1<<endl;
}
else{
for(int i=1;i<=n;i++){
if(f[i]!=i)continue;
cnt++;
if(!root)root=i;
a[i]=root;
}
cout<<cnt<<endl;
}
for(int i=1;i<=n;i++){
printf("%d ",a[i]);
}
return 0;
}
11.CF813C The Tag Game
题意:有一颗以1为根,节点数为n的树,A在1号点,B在x号点,A,B交替行动,A的目标是行动次数尽量少,B的目标是行动次数尽量多,问在这种情况下,行动的次数.
思路:求出两个点到所有的点的最短时间,a先动,所以只要a到的时间比b要早就追不上,遍历所有点,更新最大时间即可
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5;
int n,m,t,k,x,cnt,nb,mm;
int dis1[maxn],dis2[maxn];
vector<int>e[maxn];
void dfs1(int u,int fa){
for(auto v:e[u]){
if(v==fa)continue;
dis1[v]=dis1[u]+1;
dfs1(v,u);
}
}
void dfs2(int u,int fa){
for(auto v:e[u]){
if(v==fa)continue;
dis2[v]=dis2[u]+1;
dfs2(v,u);
}
}
int main(){
cin>>n>>x;
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(x,x);
dfs2(1,1);
int ans=0;
for(int i=2;i<=n;i++){
if(dis1[i]<dis2[i]){
ans=max(ans,dis2[i]*2);
}
}
cout<<ans;
}
12.CF229B Planets
题意:在宇宙里有 n n n 个星球,分别编号为 1 , 2 , . . . , n 1,2,...,n 1,2,...,n 。Jack现在在 1 1 1 号星球上,他要去 n n n 号星球。已知一些星球之间有双向的传送通道,Jack可以通过这些传送通道移动。每次传送需要一些时间,在不同的星球之间传送也可能需要不同时间。
当有其他人在使用这个星球的传送通道时,Jack无法离开这个星球。比如,如果有人在 t t t 时刻使用通道,那Jack只能在 t + 1 t+1 t+1 时刻离开(如果t+1时刻没有人在使用通道)。
现在,Jack想请你计算他最早可以在哪个时刻到达 n n n 号星球。Jack在0时刻出发。
思路:dij,到达每个点要停留一定的时间,用map保存每个点的哪个时间需要停留
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int>pii;
constexpr int maxn=1e5+5,inf=0x3f3f3f3f;
struct edge{
int v,w;
};
vector<edge>e[maxn];
map<pair<int,int>,int>wait;
int n,m,k,t,cnt,mm,dis[maxn],vis[maxn];
bool judge(){
priority_queue<pii,vector<pii>,greater<pii>>q;
memset(dis,inf,sizeof dis);
memset(vis,0,sizeof vis);
dis[1]=0;
q.push({0,1});
while(!q.empty()){
pii x=q.top();
q.pop();
int u=x.second;
int w=x.first;
if(vis[u])continue;
vis[u]=1;
while(wait[{u,w}]){
w++;
}
for(auto ss:e[u]){
int v=ss.v;
if(dis[v]>w+ss.w){
dis[v]=w+ss.w;
q.push({dis[v],v});
}
}
}
return true;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
e[u].push_back((edge){v,w});
e[v].push_back((edge){u,w});
}
for(int i=1;i<=n;i++){
cin>>k;
for(int j=1;j<=k;j++){
int res;
scanf("%lld",&res);
mm=max(mm,res);
wait[{i,res}]=1;
}
}
int ans=0;
bool sb=judge();
ans=dis[n];
if(ans<inf)cout<<ans;
else cout<<-1;
return 0;
}
13.CF1037D Valid BFS?
给定一个 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1\leq n \leq 2\cdot10^5) n(1≤n≤2⋅105)个节点的树的 n − 1 n-1 n−1条边和这棵树的一个 B F S BFS BFS序 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,…,an,判断这个 B F S BFS BFS序是否是一个从节点 1 1 1开始的合法 B F S BFS BFS序,若合法则输出 Y e s Yes Yes,否则输出 N o No No
思路:把题目要求的bfs序列保存,对树的边进行排序,bfs序列中出现早的点排前面,再进行bfs,如果有出现点不符合该bfs序则直接跳出,否则合法
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5;
int n,m,k,t,cnt;
int a[maxn],b[maxn],vis[maxn];
vector<int>e[maxn];
inline void bfs(){
queue<int>q;
q.push(1);
vis[1]=1;
while(!q.empty()){
int u=q.front();
q.pop();
if(u!=a[++cnt]){
printf("No\n");
exit(0);
}
for(auto v:e[u]){
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
printf("Yes\n");
}
int main(){
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;i++){
cin>>a[i];
b[a[i]]=i;
}
for(int i=1;i<=n;i++){
sort(e[i].begin(),e[i].end(),[](int x,int y){return b[x]<b[y];});
}
bfs();
}
14.CF1383A String Transformation 1
题意:有字符串 A A A, B B B,每次在 A A A中选取若干个相同的字母(设为 x x x),改成另一个字母(设为 y y y),需要满足 x < y x<y x<y,问将A改成B的最少操作
思路:一开始看错题目了,以为要连续的…结果是任意相同的子序列就可以,直接并查集即可,相当于不同字母间连了多少次边就需要改变多少次,a->b->c->e->k,并查集合并次数即为答案
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,t,k,cnt;
int f[30];
int find(int x){
if(f[x]==x)return x;
else return f[x]=find(f[x]);
}
void hb(int x,int y){
int fx=find(x);
int fy=find(y);
if(fx!=fy){
cnt++;
f[fx]=fy;
}
}
int main(){
cin>>t;
while(t--){
cin>>n;
string a1,a2;
cin>>a1>>a2;
for(int i=0;i<30;i++){
f[i]=i;
}
int flag=0;
for(int i=0;i<n;i++){
if(a1[i]>a2[i]){
flag=1;
break;
}
else if(a1[i]!=a2[i]){
hb(a1[i]-'a'+1,a2[i]-'a'+1);
}
}
if(flag)cout<<"-1"<<endl;
else cout<<cnt<<endl;
cnt=0;
}
}
15.CF295B Greg and Graph
Greg有一个有边权的有向图,包含 n n n 个点。这个图的每两个点之间都有两个方向的边。Greg喜欢用他的图玩游戏,现在他发明了一种新游戏:
- 游戏包含 n n n 步。
- 第 i i i 步Greg从图中删掉编号为 x i x_i xi 的点。当删除一个点时,这个点的出边和入边都会被删除。
- 在执行每一步之前,Greg想知道所有点对间最短路长度的和。最短路可以经过任何没删掉的点。换句话说,如果我们假设 d ( i , v , u ) d(i, v, u) d(i,v,u) 是在删掉 x i x_i xi 前 v v v 和 u u u 间的最短路长度,那么Greg想知道下面这个求和的值: ∑ v , u , v ≠ u d ( i , v , u ) \sum_{v, u, v \neq u} d(i, v, u) ∑v,u,v=ud(i,v,u) 。
帮帮Greg,输出每一步之前要求的值。
思路:明示要用Floyd,可是又不可能每次删掉一个点再求一次,怎么办呢?这里要用逆向思维(之前kb专题有一题也是同样的思路),从后删除的边往前加边,这样就避免了每删除一次要重新计算整张图的麻烦,而且Floyd的过程跟松弛顺序是无关的,依次加入这些点,并标记为可行点,对整张图求最短路和,如果两点都为可行点则加上这两点的最短路
(题目提示了要开longlong)
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,t,k,cnt,ans[maxn];
int dis[505][505],vis[505],now[505];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>dis[i][j];
}
}
for(int i=1;i<=n;i++){
cin>>vis[i];
}
for(int u=n;u>=1;u--){
int k=vis[u];
now[k]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j)continue;
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j)continue;
if(now[i]&&now[j])ans[u]+=dis[i][j];
}
}
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
return 0;
}
16.CF1253D Harmonious Graph
定义一张无向图是和谐的当且仅当:假设图中存在一条从 l l l 到 r r r 的路径,则图中也存在从 l l l 到 l + 1 , l + 2 , ⋯ , r − 1 l+1,l+2,\cdots,r-1 l+1,l+2,⋯,r−1 的路径。
给出一张无向图,求至少需要添加多少条边才能将其变为和谐的。
思路:求每个联通块最大编号,若后一个联通块的最小编号小于这个联通块的最大编号,则内部一定有相交部分,需要相连,ans++
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,k,t,cnt,mm;
vector<int>e[maxn];
int vis[maxn],ye[maxn];
void dfs(int u){
vis[u]=1;
mm=max(mm,u);
for(auto v:e[u]){
if(!vis[v]){
dfs(v);
}
}
}
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);
}
int ans=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
if(mm>i)ans++;
dfs(i);
}
}
cout<<ans;
return 0;
}
17.CF219D Choosing Capital for Treeland
题目描述
Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路连接了所有城市。每条道路只能单向通行。现在政府需要决定选择哪个城市为首都。假如城市i成为了首都,那么为了使首都能到达任意一个城市,不得不将一些道路翻转方向,记翻转道路的条数为k。你的任务是找到所有满足k最小的首都。
乍一看想到暴力搜,然而2e5的次方肯定t,然后写了一个假的dfs,去看了题解发现是换根dp,去学了一下,换根dp的思路是找一个点作为根进行一遍dfs预处理出信息,然后再在根上进行二次dfs根据公式进行递推每个节点的信息,从而避免了对每个节点都做根遍历的巨大复杂度。
回到这题,题目给的是有向边,但是用有向边是没法遍历整棵树的,所以我们先要存成无向边,加上边权,1代表是正向,0代表是反向,在第一遍dfs的统计中,如果走到的是反向边,则需要翻转,ans++。
在第二遍dfs中,每个节点需要翻转的次数可以由其父亲推导而来,画个图
1做完第一遍dfs后,f[1]=2,开始第二遍dfs
点1到点4,是正向边,f[4]=f[1]+1=3
点4到点3,是反向边,f[3]=f[4]-1=2
点4到点2,是反向边,f[2]=f[4]-1=2
所以答案是
2
1 2 3
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m,k,t,cnt,f[maxn];
struct edge{
int v,w;
};
vector<edge>e[maxn];
int dfs(int u,int fa){
int ans=0;
for(auto x:e[u]){
int v=x.v;
int w=x.w;
if(v==fa)continue;
if(!w)ans++;
ans+=dfs(v,u);
}
return ans;
}
void dfs2(int u,int fa){
cnt=min(f[u],cnt);
for(auto x:e[u]){
int v=x.v;
int w=x.w;
if(v==fa)continue;
f[v]=f[u]+(w==1?1:-1);
dfs2(v,u);
}
}
int main(){
cin>>n;
cnt=inf;
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
e[u].push_back((edge){v,1});
e[v].push_back((edge){u,0});
}
f[1]=dfs(1,0);
dfs2(1,0);
cout<<cnt<<endl;
for(int i=1;i<=n;i++){
if(f[i]==cnt){
printf("%d ",i);
}
}
return 0;
}