LOJ 3057 「HNOI2019」校园旅行——BFS+图等价转化
题目:https://loj.ac/problem/3057
想令 b[ i ][ j ] 表示两点是否可行,从可行的点对扩展。但不知道顺序,所以写了卡时间做数次 m2 迭代的算法,就是每次遍历所有不合法点对,枚举其出边看是否有合法的,把自己更新成合法。
可得10分。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=,M=5e5+,Tm=6e7;
int n,m,hd[N],xnt,to[M<<],nxt[M<<];
int cnt,col[N];bool a[N],b[N][N];
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void dfs(int cr)
{
col[cr]=cnt;
for(int i=hd[cr],v;i;i=nxt[i])
if(!col[v=to[i]])dfs(v);
}
void solve()
{
for(int i=;i<=n;i++)
if(!col[i]) cnt++,dfs(i);
for(int i=;i<=n;i++)b[i][i]=;
for(int i=;i<=n;i++)
for(int j=hd[i],v;j;j=nxt[j])
if(a[i]==a[v=to[j]])b[i][v]=b[v][i]=;
int pl=n*n;
//for(int lj=0,cd=0;lj<=Tm&&cd<=n;lj+=pl,cd++)
for(int lj=;lj<=Tm*;lj+=pl)
{
bool flag=;
for(int i=;i<=n;i++)
for(int j=i+;j<=n;j++)
{
if(b[i][j]||a[i]!=a[j]||col[i]!=col[j])continue;
bool fg=;
for(int l0=hd[i];l0&&!fg;l0=nxt[l0])
for(int l1=hd[j];l1;l1=nxt[l1])
if(b[to[l0]][to[l1]]){fg=;break;}
if(fg)b[i][j]=b[j][i]=;
else flag=;
}
if(!flag)break;
}
}
int main()
{
n=rdn();m=rdn();int Q=rdn();
char ch[N]; scanf("%s",ch+);
for(int i=;i<=n;i++)a[i]=ch[i]-'';
for(int i=,u,v;i<=m;i++)
{
u=rdn();v=rdn();add(u,v);add(v,u);
}
solve();int u,v;
while(Q--)
{
u=rdn();v=rdn();puts(b[u][v]?"YES":"NO");
}
return ;
}
30分暴力是这样:不是遍历不合法点对,而是遍历合法点对。
因为一个点对合法之后就没用变化,可以不用管了,所以在合法的时候把它的影响也算过,再不用管它,正确性和时间都是对的。遍历不合法点对,可能有很多失败尝试,时间没有保证。
即把合法点对压入队列,每次从队列里取出,遍历两个点出边看能否产生新的合法点对。因为点对合法之后不会有变化,所以遍历的先后之类的没有影响。
这样是 m2 的。
然后考虑把图的规模缩小。
因为发现有 “在一条边上来回走” 之类的情况,所以很多边去掉也不会影响答案。
然后从连接同色点和连接异色点的边来考虑。因为同色点之间可以来回走得到特定长度,异色点之间可以得到特定次数的颜色切换。把一个合法回文串拆成这两个部分考虑。
考虑所有连 0 类点的边构成的某个连通块。如果是二分图,则一个点到另一个点的长度任意,但一定是奇数长度或偶数长度中的一种。
如果把该连通块删边至剩下一棵树,性质不会改变。两个点之间还是任意长度、奇数或偶数中的一种。
可能本来可以较短地走过去,变成树之后不得不走很长才能走过去。不过在答案中只要在回文的另一侧多走一些就行了。
如果不是二分图,一个点到另一个点之间的长度和奇偶性都是任意的。只要在删成一棵树之后给某个点连一个自环就能让树等价于原图了。
连 1 类点的边也是一样。连异色点的边也是一样。不过连异色点的边构成的不会不是二分图。
然后图被删得剩下 O(n) 条边。刚才的做法就变成 n2 而可过了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=,M=5e5+;
int n,m,top,hd[N],xnt,to[M<<],nxt[M<<],fa[N];
bool a[N],vis[N],col[N],b[N][N],flag;
struct Ed{
int x,y;
Ed(int x=,int y=):x(x),y(y) {}
}ed[M],sta[M];
namespace G{
int hd[N],xnt,to[N<<],nxt[N<<],q[N*N][];
void add(int x,int y)
{
to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;
to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;
}
void solve()
{
int he=, tl=;
for(int i=;i<=n;i++)
{
q[++tl][]=i;q[tl][]=i;b[i][i]=;
}
for(int i=;i<=n;i++)
for(int j=hd[i],v;j;j=nxt[j])
if(a[v=to[j]]==a[i]&&i<v)
{
q[++tl][]=i;q[tl][]=v;b[i][v]=b[v][i]=;
}
while(he<tl)
{
int x=q[++he][], y=q[he][];
for(int i=hd[x],v1;i;i=nxt[i])
for(int j=hd[y],v2;j;j=nxt[j])
if(a[v1=to[i]]==a[v2=to[j]]&&!b[v1][v2])//!b[][]
{
q[++tl][]=v1;q[tl][]=v2;b[v1][v2]=b[v2][v1]=;
}
}
}
}
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
int fnd(int a){if(fa[a]==a)return a;return fa[a]=fnd(fa[a]);}
void dfs(int cr)
{
vis[cr]=;
for(int i=hd[cr],v;i;i=nxt[i])
if(a[v=to[i]]==a[cr])
{
sta[++top]=Ed(cr,v);
if(!vis[v])col[v]=!col[cr],dfs(v);
else if(col[v]==col[cr])flag=;
}
}
void init()
{
for(int i=;i<=n;i++)fa[i]=i;
for(int i=;i<=n;i++)
if(!a[i]&&!vis[i])
{
flag=;top=;dfs(i);if(flag)G::add(i,i);
for(int j=,u,v;j<=top;j++)
if((u=fnd(sta[j].x))!=(v=fnd(sta[j].y)))
G::add(sta[j].x,sta[j].y), fa[u]=v;
}
for(int i=;i<=n;i++)
if(a[i]&&!vis[i])
{
flag=;top=;dfs(i);if(flag)G::add(i,i);
for(int j=,u,v;j<=top;j++)
if((u=fnd(sta[j].x))!=(v=fnd(sta[j].y)))
G::add(sta[j].x,sta[j].y), fa[u]=v;
}
for(int i=;i<=n;i++)fa[i]=i;//
for(int i=,u,v;i<=m;i++)
if((u=fnd(ed[i].x))!=(v=fnd(ed[i].y)))
G::add(ed[i].x,ed[i].y), fa[u]=v;
}
int main()
{
n=rdn();int tp=rdn();int Q=rdn();
char ch[N]; scanf("%s",ch+);
for(int i=;i<=n;i++)a[i]=ch[i]-'';
for(int i=,u,v;i<=tp;i++)
{
u=rdn();v=rdn();add(u,v);add(v,u);
if(a[u]!=a[v])ed[++m]=Ed(u,v);
}
init(); G::solve(); int u,v;
while(Q--)
{
u=rdn();v=rdn();puts(b[u][v]?"YES":"NO");
}
return ;
}
LOJ 3057 「HNOI2019」校园旅行——BFS+图等价转化的更多相关文章
- Loj #3057. 「HNOI2019」校园旅行
Loj #3057. 「HNOI2019」校园旅行 某学校的每个建筑都有一个独特的编号.一天你在校园里无聊,决定在校园内随意地漫步. 你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你 ...
- 「loj3057」「hnoi2019」校园旅行
题目 一个n个点m条边的无向图,每个点有0 / 1 的标号; 有q个询问,每次询问(u,v)直接是否存在回文路径(可以经过重复的点和边); $1 \le n \le 5 \times 10^3 , ...
- Loj #3059. 「HNOI2019」序列
Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...
- Loj #3056. 「HNOI2019」多边形
Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...
- Loj #3055. 「HNOI2019」JOJO
Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...
- Loj 3058. 「HNOI2019」白兔之舞
Loj 3058. 「HNOI2019」白兔之舞 题目描述 有一张顶点数为 \((L+1)\times n\) 的有向图.这张图的每个顶点由一个二元组 \((u,v)\) 表示 \((0\le u\l ...
- LOJ 3059 「HNOI2019」序列——贪心与前后缀的思路+线段树上二分
题目:https://loj.ac/problem/3059 一段 A 选一个 B 的话, B 是这段 A 的平均值.因为 \( \sum (A_i-B)^2 = \sum A_i^2 - 2*B \ ...
- LOJ 3056 「HNOI2019」多边形——模型转化+树形DP
题目:https://loj.ac/problem/3056 只会写暴搜.用哈希记忆化之类的. #include<cstdio> #include<cstring> #incl ...
- LOJ 3055 「HNOI2019」JOJO—— kmp自动机+主席树
题目:https://loj.ac/problem/3055 先写了暴力.本来想的是 n<=300 的那个在树上暴力维护好整个字符串, x=1 的那个用主席树维护好字符串和 nxt 数组.但 x ...
随机推荐
- Annotations
一.介绍 注解,可以将注解看成一种特殊的接口.是一种特殊种类的元数据,它能够关联Java语言中不同元素和结构.有意思的是,在Java生态系统中大多数使用样板XML描述符的地方,注解在消除这些XML描述 ...
- Oracle DB 使用RMAN恢复目录
• 对恢复目录和RMAN 资料档案库控制文件的使用进行比较• 创建和配置恢复目录• 在恢复目录中注册数据库• 同步恢复目录• 使用RMAN 存储脚本• 备份恢复目录• 创建虚拟专用目录 RMAN 资料 ...
- HTTP 错误 500.19 - Internal Server Error v4.0.30319
1 打开运行,输入cmd进入到命令提示符窗口.2 进入到C:\Windows\Microsoft.NET\Framework\v4.0.30319 目录.3 输入aspnet_regiis.exe - ...
- C# MVC 微信支付教程系列之公众号支付
微信支付教程系列之公众号支付 今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后 ...
- 在Tomcat中部署Web项目的操作方法,maven项目在Tomcat里登录首页报404
maven项目在Tomcat里登录首页报404, 解决:编辑conf/server.xml进行配置<Host>里的<Context>标签里的path. <Context ...
- JsonLayout log4j2 json格式输出日志
如果日志输出时,想改变日志的输出形式为Json格式,可以在log4j2.xml中使用JsonLayout标签,使日志输出格式为Json格式. 前提需要Jackson的包,保证项目中包含jackson的 ...
- Java基础-常用工具类(二)
Scanner 类 java.util.Scanner 是 Java5 的新特征,我们可以通过 Scanner 类来获取用户的输入. 通过 Scanner 类的 next() 与 nextLine() ...
- java面向对象编程(六)--四大特征之继承
本文将介绍继承.方法重载和方法覆盖.其中方法重载和方法覆盖是在讲多态时必须要清楚的一个知识点. 一.继承 1.继承的概念 继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变 ...
- format 插入一个字符,%d,%n
- Task使用
Task task1 = Task.Factory.StartNew(() => { Console.WriteLine("Hello,task started by task fac ...