首页 > 其他分享 >【题解】 P8287 「DAOI R1」Flame

【题解】 P8287 「DAOI R1」Flame

时间:2022-11-29 20:33:31浏览次数:62  
标签:P8287 p2 p1 题解 mid Flame ans buf define

题面传送门

解决思路

本题解是对 这篇题解 部分内容的补充,讨论的是两种 \(\mathcal{O(m \log n)}\) 的做法。

大体思路都是一样的,先预处理出每一条边需要多少时间后才能连上,可以用 \(\text{BFS}\) 实现。

然后二分答案时间,在每个时间下连接当前已经通的边。设点 \(i\) 第一次被“点燃”的时间为 \(dis_i\),当前二分的时间为 \(t\),则 \((u,v)\) 联通的条件是 \(dis_u\le t \text{ and }dis_v\le t\),然后再新图上判断是否有环即可。


首先是 \(\text{Tarjan}\)。本题数据范围较大,而且 \(\text{Tarjan}\) 常数较大,需要快读 \(+\) 邻接表才能通过。

具体实现就是常规的 \(\text{Tarjan}\),做完一个点 \(x\) 之后,若 \(low_x<dfn_x\) ,说明它可以通过非树边到达其祖先,也就是存在环了。另外,因为二分答案做多次 \(\text{Tarjan}\),所以每次要清空!

AC Code(Tarjan)

//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define psi pair<string,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
using namespace std;
int n,m,k,u[2000005],v[2000005],b,dis[1000005];
int dfn[1000005],low[1000005],timer,head[4000005],tot,Head[1000005],Tot;
int stk[1000005],top,col[1000005];
bool mark[1000005],vis[1000005],fl;
struct node{
	int to,nxt;
}e[4000005];
struct NODE{
	int to,nxt;
}E[4000005];
queue<int> q;
inline char gc(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
	char c=gc();int res=0,f=1;
	for(;c<'0'||c>'9';c=gc()) if(c=='-')f=-1;
	for(;c>='0'&&c<='9';c=gc()) res=res*10+c-'0';
	return res*f;
}
inline void write(int x){
	static int sta[205],top=0;
	if(x<0)putchar('-'),x=-x;
	do{sta[top++]=x%10;x/=10;}while(x);
	while(top) putchar(sta[--top]+48);
}
inline void writesp(int x){
	write(x);putchar(' ');
}
inline void writeln(int x){
	write(x);putchar('\n');
}
void add(int u,int v){
	e[++tot]={v,head[u]},head[u]=tot;
}
void ADD(int u,int v){
	E[++Tot]={v,Head[u]},Head[u]=Tot;
}
void init(){
	for(int i=1;i<=m*2;i++) head[i]=0;
	for(int i=1;i<=n;i++) dfn[i]=low[i]=col[i]=0,vis[i]=0;
	tot=timer=0,fl=0;
}
void tarjan(int x,int fa){
	if(fl) return ;
	dfn[x]=low[x]=++timer;
	for(int i=head[x];i;i=e[i].nxt){
		int tmp=e[i].to;
		if(tmp==fa) continue;
		if(!dfn[tmp]){
			tarjan(tmp,x);
			if(fl) return ;
			low[x]=min(low[x],low[tmp]);
		}
		else low[x]=min(low[x],dfn[tmp]);
	}
	if(low[x]<dfn[x]){
		fl=1;
		return ;
	}
}
bool check(int mid){
	init();
	for(int i=1;i<=m;i++) if(dis[u[i]]<=mid&&dis[v[i]]<=mid){
		add(u[i],v[i]),add(v[i],u[i]);
	}
	for(int i=1;i<=n;i++) if(!dfn[i]) top=0,tarjan(i,0);
	return fl;
}
signed main(){
	n=read(),m=read(),k=read();
	for(int i=1;i<=m;i++){
		u[i]=read(),v[i]=read();
		ADD(u[i],v[i]),ADD(v[i],u[i]);
	}
	for(int i=1;i<=n;i++) dis[i]=1e9;
	for(int i=1;i<=k;i++) b=read(),q.push(b),dis[b]=0;
	while(q.size()){
		int x=q.front();q.pop();
		vis[x]=1;
		for(int i=Head[x];i;i=E[i].nxt){
			int tmp=E[i].to;
			if(vis[tmp]) continue;
			if(dis[tmp]>dis[x]+1){
				dis[tmp]=dis[x]+1;
				q.push(tmp);
			}
		}
	}
	int l=0,r=n,ans=-1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)) ans=mid,r=mid-1;	
		else l=mid+1;
	}
	if(ans==-1) puts("Poor D!");
	else writeln(ans);
	return 0;
}

然后是并查集。并查集在本题中简单很多,而且效率也更高。具体实现就是依次判断每条边,如可以连接的两点已经在同一个连通块中了,就说明有环,否则就连上。同样注意要清空。

AC Code(DSU)

//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define psi pair<string,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
using namespace std;
int n,m,k,u[2000005],v[2000005],b,dis[1000005];
int Head[1000005],Tot;
bool vis[1000005];
struct DSU{
	int fa[1000005];
	void init(int n){
		for(int i=1;i<=n;i++) fa[i]=i;
	}
	int find(int x){
		if(fa[x]==x) return x;
		return fa[x]=find(fa[x]);
	}
	void merge(int x,int y){
		fa[find(x)]=find(y);
	}
	bool query(int x,int y){
		return find(x)==find(y);
	}
}dsu;
struct NODE{
	int to,nxt;
}E[4000005];
queue<int> q;
inline char gc(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
	char c=gc();int res=0,f=1;
	for(;c<'0'||c>'9';c=gc()) if(c=='-')f=-1;
	for(;c>='0'&&c<='9';c=gc()) res=res*10+c-'0';
	return res*f;
}
inline void write(int x){
	static int sta[205],top=0;
	if(x<0)putchar('-'),x=-x;
	do{sta[top++]=x%10;x/=10;}while(x);
	while(top) putchar(sta[--top]+48);
}
inline void writesp(int x){
	write(x);putchar(' ');
}
inline void writeln(int x){
	write(x);putchar('\n');
}
void ADD(int u,int v){
	E[++Tot]={v,Head[u]},Head[u]=Tot;
}
bool check(int mid){
	dsu.init(n);
	for(int i=1;i<=m;i++) if(dis[u[i]]<=mid&&dis[v[i]]<=mid){
		if(dsu.query(u[i],v[i])) return 1;
		else dsu.merge(u[i],v[i]);
	}
	return 0;
}
signed main(){
	n=read(),m=read(),k=read();
	for(int i=1;i<=m;i++){
		u[i]=read(),v[i]=read();
		ADD(u[i],v[i]),ADD(v[i],u[i]);
	}
	for(int i=1;i<=n;i++) dis[i]=1e9;
	for(int i=1;i<=k;i++) b=read(),q.push(b),dis[b]=0;
	while(q.size()){
		int x=q.front();q.pop();
		vis[x]=1;
		for(int i=Head[x];i;i=E[i].nxt){
			int tmp=E[i].to;
			if(vis[tmp]) continue;
			if(dis[tmp]>dis[x]+1){
				dis[tmp]=dis[x]+1;
				q.push(tmp);
			}
		}
	}
	int l=0,r=n,ans=-1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)) ans=mid,r=mid-1;	
		else l=mid+1;
	}
	if(ans==-1) puts("Poor D!");
	else writeln(ans);
	return 0;
}

标签:P8287,p2,p1,题解,mid,Flame,ans,buf,define
From: https://www.cnblogs.com/binary1110011/p/16936589.html

相关文章

  • 2022 ICPC 济南站 L 题题解
    题意给定一棵\(n\)个点有边权的无根树,有\(q\)次询问,每次给定\(l,r\),求\[\min_{l\leu<v\ler}\{\operatorname{dist}(u,v)\}.\]数据范围:\(1\len\le2\times10^5......
  • 「题解」Codeforces 1765L Project Manager
    写了两个小时写吐了,你告诉我这玩意2400?如果不管假期的话,那么每一周必然会有一个项目跟进一次进度。那么答案就是线性的。即使有假期的存在也没关系,每个假期顶多就只会拖......
  • angular FormArray 中嵌套 FormGroup 问题解决
    官方例子里说了FormArray可以嵌套group或者array,但只给了control的实例,这里记录一下嵌套groupts文件:import { Component } from '@angular/core';import { For......
  • NOIP 2022 题解
    rp++juruo的noip真实成绩:0+0+0+0=0pts.题目大意洛谷有,这里就不放了。T1.种花可以维护每一个点向下最多延伸多长$xia_i$,向右延伸最多多长$you_i$,这样C就好求了,可以维......
  • 题解 P4448
    如果这不是一道原题,这道题出的还不错,是个比较毒瘤的数数。由于我太菜了反正我自己没有做出来后面的dp,zyf巨佬教的。不过听说合肥六中某巨佬当年也没做出来,平衡了雾但问......
  • AT_otemae2019_a 寝坊だ!ピ太郎! (You overslept, Pitaro) 题解
    题目大意:给出两个数 a,b 如果 a+0.5>b,输出 1,否则输出 0。a,b 均为整数。思路:这是一道模拟题,模拟即可。代码:注意:要开浮点型!#include<bits/stdc++.h>using......
  • CF1119E Pavel and Triangles 题解
    题面PavelandTriangles题面翻译给定n种木棍,第i+1种有ai​个,长度为2^i,求用这些木棍可以同时拼出多少个三角形(不可重复使用同一根)输入第一行n,第二行n个整数a0,a1,a2.........
  • P8867 [noip2022]建造军营 题解
    题意:给定一张图,选择一些点和一些边,使得割断任意没有选的边,被选中的点仍连通。对\(10^9+7\)取膜。\(n\leq5\cdot10^5\)这篇题解会略讲缩边,详讲自己推dp状态与......
  • Oracle的会话进程解锁及问题解决方法
    首先用dba权限的用户登陆数据库1、select*fromv$locked_object查出被锁定的对象,其中object_id是对象的ID,session_id是被锁定对象有sessionID;2、selectobject_name......
  • 『题解』Codeforces 1758B XOR = Average
    Description构造一个\(a\)序列,使\(a_1\oplusa_2\oplusa_3\oplus\cdots\oplusa_n=\dfrac{sum(a)}{n}\)。Solution第一眼看到这道题,我想到的是分情况讨论。......