题目描述

  有一棵\(n\)个点的树,还有\(m\)个物品。

  你要把每个物品放在树上的一个点上(两个物品可以放在同一个点)。

  有\(q\)个限制:\(a,b\)两个物品在路上的最短路经过\(c\)。

  要你构造一组合法的方案。

  \(n,m\leq 250\)

题解

  很容易想到2-sat。

  但是把点看成"物品\(x\)放在\(y\)上"会找不到合法解。

  所以要把点看成“物品\(x\)在以\(y\)为根的子树内”,这样根就是必须选的。

  连边的话(下面只列出一半的边):\(x\rightarrow f_x,x\rightarrow !y\)(\(y\)是\(x\)的兄弟)(这些边可以弄一些前缀后缀的点优化)

  和询问有关的:

  1.\(a\neq b\):\((a,x)\rightarrow !(b,x)\)(\(x\)是\(c\)的儿子),\(!(a,c)\rightarrow (b,c)\)。

  2.\(a=b\):\(!(a,c)\rightarrow(a,c)\),\((a,x)\rightarrow !(a,x)\)(\(x\)是\(c\)的儿子)。

  点数为\(O(nm)\)

  边数为\(O(nm^2)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
struct graph
{
int h[400010];
int v[40000010];
int t[40000010];
int n;
graph()
{
n=0;
memset(h,0,sizeof h);
}
void add(int x,int y)
{
n++;
v[n]=y;
t[n]=h[x];
h[x]=n;
}
};
graph g;
vector<int> t[260],t2;
int n,m,q;
int id(int x,int y)//x=物品,y=点
{
return (y-1)*m+x;
}
int cnt;
int pre[260],suf[260];
void add0(int k1,int x,int k2,int y)
{
g.add(id(k1,x)*2-1,id(k2,y)*2);
g.add(id(k2,y)*2-1,id(k1,x)*2);
}
void add4(int k1,int x,int k2,int y)
{
g.add(id(k1,x)*2,id(k2,y)*2-1);
g.add(id(k2,y)*2,id(k1,x)*2-1);
}
void add1(int k1,int x,int k2,int y)
{
g.add(id(k1,x)*2-1,id(k2,y)*2-1);
g.add(id(k2,y)*2,id(k1,x)*2);
}
void add2(int k,int x)
{
g.add(id(k,x)*2,id(k,x)*2-1);
}
void add3(int k,int x)
{
g.add(id(k,x)*2-1,id(k,x)*2);
}
int f[300];
int d[300];
void dfs(int x,int fa,int dep)
{
t2.clear();
f[x]=fa;
d[x]=dep;
for(auto v:t[x])
if(v!=fa)
t2.push_back(v);
t[x]=t2;
for(auto v:t[x])
dfs(v,x,dep+1);
}
int b[400010];
int e[400010];
int ti,tot,top;
int st[400010];
int dfn[400010];
int low[400010];
int c[300][300][300];
void dfs(int x)
{
low[x]=dfn[x]=++ti;
st[++top]=x;
b[x]=1;
for(int i=g.h[x];i;i=g.t[i])
{
if(b[g.v[i]]!=2)
{
if(!b[g.v[i]])
dfs(g.v[i]);
low[x]=min(low[x],low[g.v[i]]);
}
}
if(low[x]>=dfn[x])
{
int v;
tot++;
do
{
v=st[top--];
e[v]=tot;
b[v]=2;
}
while(v!=x);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
scanf("%d%d%d",&n,&m,&q);
int x,y,z;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
t[x].push_back(y);
t[y].push_back(x);
}
dfs(1,0,1);
cnt=n;
for(int i=1;i<=n;i++)
{
if(i!=1)
for(int k=1;k<=m;k++)
add1(k,i,k,f[i]);
int sz=t[i].size();
for(int j=0;j<sz;j++)
{
pre[j]=++cnt;
for(int k=1;k<=m;k++)
add1(k,t[i][j],k,pre[j]);
if(j)
for(int k=1;k<=m;k++)
add1(k,pre[j-1],k,pre[j]);
}
for(int j=sz-1;j>=0;j--)
{
suf[j]=++cnt;
for(int k=1;k<=m;k++)
add1(k,t[i][j],k,suf[j]);
if(j!=sz-1)
for(int k=1;k<=m;k++)
add1(k,suf[j+1],k,suf[j]);
}
for(int j=0;j<sz;j++)
{
if(j)
for(int k=1;k<=m;k++)
add0(k,t[i][j],k,pre[j-1]);
if(j!=sz-1)
for(int k=1;k<=m;k++)
add0(k,t[i][j],k,suf[j+1]);
}
}
for(int i=1;i<=m;i++)
add2(i,1);
memset(b,0,sizeof b);
for(int i=1;i<=q;i++)
{
scanf("%d%d%d",&x,&y,&z);
if(x>y)
swap(x,y);
if(c[x][y][z])
continue;
c[x][y][z]=1;
if(x==y)
{
add2(x,z);
for(auto v:t[z])
add3(x,v);
}
else
{
for(auto v:t[z])
add0(x,v,y,v);
add4(x,z,y,z);
}
}
ti=0;
tot=0;
top=0;
for(int i=1;i<=2*m*cnt;i++)
if(!b[i])
dfs(i);
for(int i=1;i<=m;i++)
{
int ans=1;
for(int j=1;j<=n;j++)
if(e[2*id(i,j)-1]<e[2*id(i,j)])
if(d[j]>d[ans])
ans=j;
printf("%d ",ans);
}
return 0;
}

【XSY2750】Mythological V 2-sat的更多相关文章

  1. 【XSY2786】Mythological VI 数学

    题目描述 有\(1\sim n\)一共\(n\)个数.保证\(n\)为偶数. 你要把这\(2n\)个数两两配对,一共配成\(n\)对.每一对的权值是他们两个数的和. 你想要知道这\(n\)对里最大的权 ...

  2. 【XSY2751】Mythological IV 线性插值

    题目描述 已知\(f(x)\)为\(k\)次多项式. 给你\(f(0),f(1),\ldots,f(k)\),求 \[ \sum_{i=1}^nf(i)q^i \] \(k\leq 500000,n\ ...

  3. 【转】grep -v grep

    1.grep 是查找含有指定文本行的意思,比如grep test 就是查找含有test的文本的行 2.grep -v 是反向查找的意思,比如 grep -v grep 就是查找不含有 grep 字段的 ...

  4. MT【330】u,v,w法

    已知$a^2+b^2+c^2=1$求$abc(a+b+c)$的最小值.(2018辽宁预赛解答压轴题) 不妨设$a+b+c=3u,ab+bc+ca=3v^2,abc=w^3$,令$u^2=tv^2$要求 ...

  5. 【XSY2787】Mythological VII 贪心

    题目描述 有两个指针\(l,r\),初始时\(l=r=k\) 给你\(a_1,\ldots,a_n\),你要移动\(l,r\),\(l\)只能每次向左移一个数,\(r\)只能向右移一个数,要求任意时刻 ...

  6. 【学时总结】◆学时·V◆ 逆元法

    ◆学时·V◆ 逆元法 □算法概述□ 逆元运算是模运算中的一个技巧,一般用于解决模运算的除法问题.模运算对于加.减.乘是有封闭性的,即 (a±b)%m=a%m±b%m,以及 (a×b)%m=a%m×b% ...

  7. 【原创】如何通过-y和-v使用库文件

    在进行仿真时,经常遇到设计代码中需要调用一些标准的库文件,但是在设计的编译列表filelist中却没有相应的库文件,这时为了完成仿真,需要设计人员提供对应的库文件或者库文件所在的路径,然后仿真时将这些 ...

  8. 【pwn】V&N2020 公开赛 simpleHeap

    [pwn]V&N2020 公开赛 simpleHeap 1.静态分析 首先libc版本是ubuntu16的2.23版本,可以去buu的资源处下载 然后checksec一下,保护全开 拖入IDA ...

  9. 【Leetcode】1340. Jump Game V 【动态规划/记忆性搜索】

    Given an array of integers arr and an integer d. In one step you can jump from index i to index: i + ...

随机推荐

  1. C++与Java,C#的异同(一):值,地址,引用

    Java,C#已经比较熟悉,最近在从0开始自学C++.学习过程中必然会与Java,C#进行对比,有吐槽,也有点赞. 先来讲讲最基本也是最重要的部分:参数传递的方式. 对于类型, Java分基本类型.复 ...

  2. H5 67-清除浮动方式三

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. Python-类的组合与重用

    软件重用的重要方式除了继承之外还有另外一种方式,即:组合 组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合 1.继承的方式 通过继承建立了派生类与基类之间的关系,它是一种'是'的关系 ...

  4. SQL Server中JOIN的使用方法总结

    JOIN 分为:内连接(INNER JOIN).外连接(OUTER JOIN).其中,外连接分为:左外连接(LEFT OUTER JOIN).右外连接(RIGHT OUTER JOIN).全外连接(F ...

  5. java随笔1 Ctrl+1补全

    Ctrl+1补全变量时,如果补全后的不是自己想要的, 比如:补全后这样 修改后 这时要对更改变量进行Ctrl+1补全路径 并且后者要进行Ctrl+1强转

  6. Day5-1 面向对象和面向过程

    摘要: 类的定义 类的增删改查 对象的增删改查 对象的查找和绑定 面向对象和面向过程的区别: 1.面向过程就像是工厂的流水线,按部就班的有序的工作. 优点:把复杂的问题简单化 缺点:可扩展性差.一个步 ...

  7. redis 的简单命令

    以下实例讲解了如何启动 redis 客户端: 启动 redis 客户端,打开终端并输入命令 redis-cli.该命令会连接本地的 redis 服务. $redis-cli redis > re ...

  8. Navicat 远程连接Docker容器中的mysql 报错:1251 - Client does not support authentication protocol 解决办法。

    出现这个问题 首先进入 1.docker exec -it mysql02 bash      //mysql02是mysql容器的别名 2.mysql -uroot -p 3.输入密码 4.进入my ...

  9. Hbase 架构体系

    有2个节点进程,一个是master,另一是regionserver.

  10. python爬虫之redis环境简单部署

    Redis 简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久 ...