zoukankan      html  css  js  c++  java
  • 【洛谷P6378】Riddle

    题目

    题目链接:https://www.luogu.com.cn/problem/P6378
    (n) 个点 (m) 条边的无向图被分成 (k) 个部分。每个部分包含一些点。
    请选择一些关键点,使得每个部分有一个关键点,且每条边至少有一个端点是关键点。
    (n,m,kleq 10^6)

    思路

    看到一堆限制不难想到 2-sat。
    把每一个点 (x) 拆成 (x)(x') 分别表示选与不选,对于图中一条边 ((x,y)),连边 ((x',y),(y',x))
    接下来对于每一个部分,选择了其中一个点,那么这个部分的其他点都不可以选。但是直接连边空间是 (O(n^2)) 的,无法接受。
    我们再记 (l_x)(l_x') 表示点 (x) 所在的部分中,(x) 以及之前的点恰好有一个被选,没有任何被选。显然有连边 ((x,l_x),(l_x',x'))。考虑对于这个部分相邻的两个点 (x,y) 的连边:

    • ((y,l_x'),(l_x,y'))。如果选 (y) 那么前面都不能选;如果前面选了那么不能选 (y)
    • ((l_x,l_y),(l_y',l_x'))。如果这个部分 (x) 之前选了,那么 (y) 之前必然选了;如果 (y) 之前没选,那么 (x) 之前必然没选。

    然后跑 2-sat 就完事了。
    但是这样只是保证了对于每一条边都选了至少一个点,且每一个部分至多选一个点,看起来并不保证每一个部分都选了点。考虑什么时候会出现一个部分没有选点的情况,当且仅当这一个块中任意两个点都没有连边(否则这两个点必须选择一个)。那么如果出现这种情况,且这个部分没有选点,只需要随便拿这一个部分的点选上即可。
    至此,题目的所有限制都满足了。
    时间复杂度 (O(n+m))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=4000010;
    int n,m,k,cnt,tot,head[N],dfn[N],low[N],col[N];
    bool vis[N];
    stack<int> st;
    
    int read()
    {
    	int d=0; char ch=getchar();
    	while (!isdigit(ch)) ch=getchar();
    	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
    	return d;
    }
    
    struct edge
    {
    	int next,to;
    }e[N*3];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    void tarjan(int x)
    {
    	dfn[x]=low[x]=++tot;
    	st.push(x); vis[x]=1;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (!dfn[v])
    		{
    			tarjan(v);
    			low[x]=min(low[x],low[v]);
    		}
    		else if (vis[v])
    			low[x]=min(low[x],dfn[v]);
    	}
    	if (low[x]==dfn[x])
    	{
    		int y; cnt++;
    		do {
    			y=st.top(); st.pop();
    			col[y]=cnt; vis[y]=0;
    		} while (y!=x);
    	}
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	n=read(); m=read(); k=read();
    	for (int i=1;i<=m;i++)
    	{
    		int x=read(),y=read();
    		add(x+n,y); add(y+n,x);
    	}
    	for (int i=1;i<=k;i++)
    	{
    		int t=read()-1,x=read(),y;
    		add(x,x+2*n); add(x+3*n,x+n);
    		while (t--)
    		{
    			y=read();
    			add(y,y+2*n); add(y+3*n,y+n);
    			add(y,x+3*n); add(x+2*n,y+n);
    			add(x+2*n,y+2*n); add(y+3*n,x+3*n);
    			x=y;
    		}
    	}
    	tot=0;
    	for (int i=1;i<=4*n;i++)
    		if (!dfn[i]) tarjan(i);
    	for (int i=1;i<=n;i++)
    		if (col[i]==col[i+n] || col[i+2*n]==col[i+3*n])
    			return printf("NIE"),0;
    	printf("TAK");
    	return 0;
    }
    
  • 相关阅读:
    时装画基础知识--如何画人体
    马士兵java视频学习顺序
    Mysql 中文字符乱码问题
    zendstudio 设置默认编码 utf-8 gbk
    MYSQL 本地无ROOT权限 忘记密码
    windows 3389 远程
    windows 老掉牙CMD的命令
    mysql-常用注入渗透手法
    ubuntu 添加多个IP
    windows下简单配置apache
  • 原文地址:https://www.cnblogs.com/stoorz/p/14887210.html
Copyright © 2011-2022 走看看