5.1考试题解
5.1考试题解
现在是大图论时代!!!!
T1 Travel

数据如上
我们发现,根据题面描述,每个节点有且仅有一条出边.此时就会出现如下唯三种情况
-
形成一个环
-
形成一条链
-
形成一条最终指向一个环的链
同时有了如下性质
- 一个在环内的节点最大能得到的总值其实就是这个环所有点权之和(很好证明,因为每个节点只会有一条出边,所以如果有一个节点指向了环外一个节点,则不存在此环)
- 一个在链上的节点最大能得到的总值就是走到底的点权的总和(容易发现如果走到环上一点就不必继续走了,可以直接不继续累加下去了,此时可以直接加上这个环内的点权之和)
此时就有了想法
找环
求环内总值
对于环外点走到底然后赋值
对于找环,很容易想到Top Sort对吧(O(n+m)的时间复杂度还真是让人喜爱(雾))
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
const int INF=1e4;
int n,A[N],in[N];
int to[N];
int vis[N],val[N];
void topsort(){
queue<int>q;
for(int i=1;i<=n;i++){
if(in[i] == 0){q.push(i);vis[i]=2;}
}
while(q.size()){
int t = q.front(),v=to[t];q.pop();in[v]--;
if(in[v]==0){vis[v] = 2;q.push(v);}
}
}//拓扑排序
int dfs(int now,int sum){
int v=to[now];
if(vis[v]==1)
return val[now]=val[now]+sum;
vis[now] = 1;
val[now] = dfs(v,sum+val[now]);
return val[now];
}//dfs对于环内的所有节点进行赋值
void dfs0(int now){
int v=to[now];
if(vis[v] == 1){
val[now] = val[v]+A[now];return;
}
dfs0(v);
val[now] = val[v]+A[now];
}//dfs对于所有非环内节点进行赋值
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>A[i];
}
for(int u=1;u<=n;u++){
int v;cin>>v;
to[u]=v;
in[v]++;
}
topsort();
//存图,拓扑
for(int i=1;i<=n;i++)val[i]=A[i];
for(int i=1;i<=n;i++){
if(vis[i]!=2&&vis[i]!=1){
vis[i]=1;
dfs(i,0);
}
}//找到环上节点,赋值
for(int i=1;i<=n;i++){
if(vis[i] == 2){
vis[i] = 1;
dfs0(i);
}
}//找到环外节点,标记为环上节点,赋值
for(int i=1;i<=n;i++)cout<<val[i]<<"\n";
return 0;
}
(挺恶心的,调了半天)
T2 Sns
我们发现,对于一个图中,如果点对(u,v,w)不在同一连通块,则一定不可能连接;在同一连通块则一定可以连接。所以我们想到可以找一个连通块,统计连通块中有几个节点,然后计算可以选择多少组点对(排列组合Right?),最后减去m就好了。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
int n,m;
int fa[N];
int size[N];
int find(int x){
return fa[x] == x?x:fa[x] = find(fa[x]);
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
fa[i]=i,size[i] = 1;
}
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
int a=find(x),b=find(y);
if(a!=b)
fa[a] = b,size[b]+=size[a];
}
int ans=0;
for(int i=1;i<=n;i++){
if(fa[i] == i){
ans+=size[i]*(size[i]-1)/2;
}
}
ans-=m;
cout<<ans;
return 0;
}
T3 Junction

数据1如上图
我们发现,一条同时进过a,b的路径,左端点一定在a左侧,右端点一定在b右侧(就图而言)
假设a左侧有al个节点,b右侧有br个节点,则ans一定等于\(al\times br\)(排列组合Right?)
最后,对于al,br可以通过dfs隔离的方法求得
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,m,a,b;
int sum;
int vis[N];
vector<int>g[N];
void dfs(int now){
for(auto i:g[now]){
if(vis[i])continue;
sum++;vis[i] = 1;
dfs(i);
}
}
signed main(){
cin>>n>>m>>a>>b;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
int al=0;
vis[a] = 1;
vis[b] = 1;sum=2;
dfs(a);al+=sum;al=n-al;
// cout<<a
memset(vis,0,sizeof vis);
int br=0;
sum=2;
vis[a]=vis[b]=1;
dfs(b);br+=sum;br=n-br;
cout<<br*al;
return 0;
}
T4 Line
哎,不会就打暴力嘛,50分又不是接受不了~
Code
#include<bits/stdc++.h>
using namespace std;
const double INF=1e6;
const int FUCK_CCF=0;
const int N=2e5;
int n,m;
struct node{
int w,b,v;
};
int ans,vis[N];
vector<node>g[N];
void dfs(int now,double sw,double bb){
// cout<<now<<" ";
if(now == n){
// cout<<double(bb/sw);
ans = max(ans,int(double(bb/sw)*INF));
return;
}
for(node i:g[now]){
// cout<<"$";
if(vis[i.v])continue;
vis[i.v] = 1;
dfs(i.v,sw+i.w,(double)min(bb,(double)i.b));
vis[i.v] = 0;
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w,b;
cin>>u>>v>>w>>b;
g[u].push_back(node{w,b,v});
g[v].push_back(node{w,b,u});
}
dfs(1,FUCK_CCF,0x3f3f3f);
cout<<ans;
return FUCK_CCF;
}
当然正解也有

日常犯懒

浙公网安备 33010602011771号