洛谷 P2783 有机化学之神偶尔会做作弊

原题链接
 

题目本质:图论,强连通分量,最近公共祖先LCA

这道题之前做过,那会是打暴力打了好像是27分还是37分来着,这次再做题面改了没看出来,放在T4以为很难,实际上难度还可以,连暴力都没打。下面是说说解题思路:

首先我们会由 "所有的环状碳都变成了一个碳" 想到要缩点。但是无向图怎么缩点呢?我们可以按照原来无向图那样缩点,但要注意的一点是 to!=fa[x]。因为这是无向图,可能有的边会直接连向他父亲,假如我们要走这条边的话,就会重复搜,就这样一直无限循环下去。剩下的就和有向图的缩点没什么区别了。接着我们就要考虑每个询问。我们把所有的环去掉后,就会得到一个有向无环图(树)。那么问题就会转化为树上问题。

先给暴力代码吧

27pts

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100005;
ll read() {
	ll x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
	return x * f;
}
struct Edge {
	ll from, to, pre;
} edge[N], e[N];

ll last[N];
ll cnt = 1, nlast[N];
ll ncnt = 1;;

void add(ll x, ll y) {
	++cnt;
	edge[cnt].from = x;
	edge[cnt].to = y;
	edge[cnt].pre = last[x];
	last[x] = cnt;
}
void nadd(ll x, ll y) {
	++ncnt;
	e[ncnt].from = x;
	e[ncnt].to = y;
	e[ncnt].pre = nlast[x];
	nlast[x] = ncnt;
}
ll dfn[N], low[N];
bool bridge[N];
ll num = 0, qnum[N];
void tarjan(ll x, ll y) {
	dfn[x] = low[x] = ++num;
	for (ll i = last[x]; i; i = edge[i].pre) {
		ll v = edge[i].to;
		if (!dfn[v]) {
			tarjan(v, i);
			low[x] = min(low[x], low[v]);
			if (low[v] > dfn[x]) {
				bridge[i] = bridge[i ^ 1] = true;
				++qnum[0];
				qnum[qnum[0]] = i;
			}
		} else if ((i ^ 1) != y) low[x] = min(low[x], dfn[v]);
	}
	return;
}

ll sltnum = 0, belong[N];

void dfs(long long x) {
	belong[x] = sltnum;
	for (long long i = last[x]; i; i = edge[i].pre) {
		if (bridge[i] || belong[edge[i].to]) continue;
		dfs(edge[i].to);
	}
	return;
}

ll f[N][18], deep[N];

void st(ll x, ll fa) {
	deep[x] = deep[fa] + 1;
	for (int i = nlast[x]; i; i = e[i].pre) {
		if (e[i].to == fa) continue;
		f[e[i].to][0] = x;
		st(e[i].to, x);
	}
	ll len = log2(deep[x]) + 1;
	for (int i = 1; i <= len; ++i){
		f[x][i] = f[f[x][i - 1]][i - 1];
	}
	return;
}

ll LCA(ll x,ll y) {
	if (deep[x] > deep[y]){
		swap(x, y);
	}
	int len = log2(deep[y] - deep[x]) + 1;
	for (int i = len; i >= 0; --i) {
		if (deep[f[y][i]] >= deep[x]) y = f[y][i];
	}
	if (x == y) return x;
	len = log2(deep[x]) + 1;
	for (int i = len; i >= 0; --i) {
		if (f[x][i] != f[y][i]) {
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}

void write(ll x) {
	if (x == 0) {
		printf("0");
		return;
	}
	if (x == 1) {
		printf("1");
		return;
	}
	write(x >> 1);
	printf("%lld", x % 2);
}

ll n, m, q;
int main() {
	n = read();
	m = read();
	ll u, v;
	for (int i = 1; i <= m; ++i) {
		u = read();
		v = read();
		add(u, v);
		add(v, u);
	}
	tarjan(1, 0);
	for (int i = 1; i <= n; ++i) {
		if (!belong[i]) {
			++sltnum;
			dfs(i);
		}
	}
	for (int i = 1; i <= qnum[0]; ++i) {
		nadd(belong[edge[qnum[i]].from], belong[edge[qnum[i]].to]);
		nadd(belong[edge[qnum[i]].to], belong[edge[qnum[i]].from]);
	}
	q = read();
	st(1, 0);
	ll a, b;
	for (int i = 1; i <= q; ++i) {
		a = read();
		b = read();
		a = belong[a];
		b = belong[b];
		write(deep[a] + deep[b] - 2 * deep[LCA(a, b)] + 1);
		printf("\n");
	}
	return 0;
}

18pts

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e4+10;
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;
}
int t[10010];
void ot(int x) {
	int xx = 0;
	for (; x; x >>= 1) {
		xx++;
		if (x & 1) t[xx] = 1;
		else t[xx] = 0;
	}
	for (int i = xx; i >= 1; i-- )  printf("%d", t[i]);
	printf("\n");
}
int head2[N], tot;
struct node {
	int to, nxt;
} e[N ], ne[N ];
void add(int a, int b) {
	tot++;
	e[tot].nxt = head2[a];
	e[tot].to = b;
	head2[a] = tot;
}
int n, m;
int head22[N], tot2;
void add2(int a, int b) {
	tot++;
	ne[tot].to = b;
	ne[tot].nxt = head22[a];
	head22[a] = tot;
}
int f[N][20], dep[N];
int cnt;
void pre() {
	for (int j = 1; (1 << j) <= cnt; j++) {
		for (int i = 1; i <= cnt; i++) {
			if (f[i][j - 1] != -1) {
				f[i][j] = f[f[i][j - 1]][j - 1];
			}
		}
	}
}
void dfs(int x, int fa) { //当前节点,当前节点的父节点
	dep[x] = dep[fa] + 1;
	for (int i = head22[x]; i != -1; i = ne[i].nxt) {
		int y = ne[i].to;
		if (y != fa) {
			f[y][0] = x;
			dfs(y, x);
		}
	}
}
int lca(int x, int y) {
	if (dep[x] > dep[y]) swap(x, y);
	int k = log2(dep[y]) + 1;
	for (int i = k; i >= 0; i--) {
		if (dep[y] - (1 << i) >= dep[x]) y = f[y][i];
	}
	if (x == y) {
		return x;
	}
	for (int i = k; i >= 0; i--) {
		if (f[x][i] != -1 && f[x][i] != f[y][i]) {
			x = f[x][i];
			y = f[y][i];
		}
	}
	//最后X是最近公共祖先的子节点
	return f[x][0];
}
//缩点,建新图
int root;
int scc[N];
int dfn[N], low[N], tt;
int vis[N] = {false};
int in[N];
stack<int> stk;
queue<int> q[N];
//bool pan[N]={false};
void ta(int x, int fa) {
	dfn[x] = low[x] = ++tt;
	//if(!pan[x]){
	//	cnt++;q[cnt].push(x);
	//cout<<x<<"hkjh"<<endl;
	//	return;
	//}
	stk.push(x);
	in[x] = 1;
	int child = 0;
	for (int i = head2[x]; i; i = e[i].nxt) {
		//sas=true;
		int y = e[i].to;
		if (y == fa) continue;
		//if(x==y)
		if (!dfn[y]) {
			ta(y, x);
			low[x] = min(low[x], low[y]);
			//if(low[y]>=dfn[x]){
			//	child++;
			//	if(child>1||x!=root){
			//		vis[x]=1;
			//	}
			///	cnt++;
			//	while(1){
			//		int z=stk.top();stk.pop();
			//		scc[z]=cnt;
			//		q[cnt].push(z);
			//		if(z==y) break;
			//	}
			//	q[cnt].push(x);
			//	scc[x]=cnt;
			//
		} else if (in[y]) low[x] = min(low[x], dfn[y]);
	}
	if (dfn[x] == low[x]) {
		cnt++;
		int y;
		do {
			y = stk.top();
			stk.pop(), in[y] = 0;
			scc[y] = cnt;
		} while (y != x);
	}
}
signed main() {
	n = read(), m = read();
	for (int i = 1; i <= m; i++) {
		int a = read(), b = read();
		add(a, b);
		add(b, a);
	}
	for (int i = 1; i <= n; i++) {
		if (!dfn[i]) {
			root = i, ta(i, 0);
		}
	}
	//cout<<cnt<<endl;
	memset(head22, -1, sizeof(head22));
	memset(f, -1, sizeof f);
	for (int x = 1; x <= n; x++) {
		for (int i = head2[x]; i; i = e[i].nxt) {
			int y = e[i].to;
			if (scc[x] != scc[y]) add2(scc[x], scc[y]); //cout<<scc[x]<<"   "<<scc[y]<<endl;
		}
	}
	dfs(1, 0);
	pre();
	int T = read();
	for (int i = 1; i <= T; i++) {
		int a = read(), b = read();
		int k = lca(a, b);
		//cout<<k<<endl;
		ot(dep[a] + dep[b] + 1 - 2 * dep[k]);
		//puts("");
	}
	return 0;
}

AC

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n,m,u,v,x,y,tot,sum,cnt,num,topp,T;
int dep[N],fa[N],size[N],top[N],head[N],hed[N];
int shu[N],dfn[N],low[N],sta[N],son[N];
int t[N];
bool vis[N];
inline int read()//标准快读
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
	return s * w;
}
struct node{int to,net;}e[N<<1],edge[N<<1];//为了压行不择手段
void add(int x,int y)
{
	e[++tot].to = y;
	e[tot].net = head[x];
	head[x] = tot;
}
void add_(int x,int y)//建新图上的边
{
	edge[++sum].to = y;
	edge[sum].net = hed[x];
	hed[x] = sum;
}
void tarjain(int x,int fa)//缩点
{
	dfn[x] = low[x] = num++;
	sta[++topp] = x; vis[x] = 1;
	for(int i = head[x]; i; i = e[i].net)
	{
		int to = e[i].to;
		if(to == fa) continue;//特判是不是联向他父亲得边
		if(!dfn[to])
		{
			tarjain(to,x);
			low[x] = min(low[x],low[to]);
		}
		else if(vis[to])
		{
			low[x] = min(low[x],dfn[to]);
		}
	}
	if(dfn[x] == low[x])//求强联通分量
	{
		cnt++; int y;
		do
		{
			y = sta[topp--];
			//size[cnt]++;
			shu[y] = cnt;
			vis[y] = 0;
		}while(x != y);
	}
}
void get_tree(int x)//树剖第一遍DFS求重儿子
{
	dep[x] = dep[fa[x]] + 1; size[x] = 1;
	for(int i = hed[x]; i; i = edge[i].net)
	{
		int to = edge[i].to;
		if(to == fa[x]) continue;
		fa[to] = x;
		get_tree(to);
		size[x] += size[to];
		if(size[to] > size[son[x]]) son[x] = to;
	}
}
void dfs(int x,int topp)//树剖第二遍DFS求每条链的顶端
{
	top[x] = topp;
	if(son[x]) dfs(son[x],topp);
	for(int i = hed[x]; i; i = edge[i].net)
	{
		int to = edge[i].to;
		if(to == fa[x] || to == son[x]) continue;
		dfs(to,to);
	}
}
int lca(int x,int y)//树剖求LCA
{
	while(top[x] != top[y])
	{
		if(dep[top[x]] < dep[top[y]]) swap(x,y);
		x = fa[top[x]];
	}
	if(dep[x] < dep[y]) return x;
	else return y;
}
void shuchu(int x)//二进制转化
{
    int xx = 0;
    for(; x; x>>=1)
    {
    	xx++;
    	if(x & 1) t[xx] = 1;
    	else t[xx] = 0;
    }
    for (int i = xx; i >= 1; i--) printf("%d",t[i]);
    printf("\n");
}
int main()
{
	n = read(); m = read();
	for(int i = 1; i <= m; i++)
	{
		u = read(); v = read();
		add(u,v); add(v,u);
	}
	for(int i = 1; i <= n; i++)
	{
		if(!dfn[i]) tarjain(i,0);
	}
	for(int i = 1; i <= n; i++)//缩点
	{
		for(int j = head[i]; j; j = e[j].net)
		{
			int to = e[j].to; 
			if(shu[to] != shu[i])
			{
				add_(shu[i],shu[to]);
			}
		}
	}
	get_tree(1);
	dfs(1,1);
	T = read();
	while(T--)
	{
		x = read(); y = read();
		int Lca = lca(shu[x],shu[y]);
		int ans = dep[shu[x]] + dep[shu[y]] - 2 * dep[Lca] + 1;//计算每个询问的答案
		shuchu(ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值