题目:https://loj.ac/problem/3057

想令 b[ i ][ j ] 表示两点是否可行,从可行的点对扩展。但不知道顺序,所以写了卡时间做数次 m2 迭代的算法,就是每次遍历所有不合法点对,枚举其出边看是否有合法的,把自己更新成合法。

可得10分。

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. int rdn()
  6. {
  7. int ret=;bool fx=;char ch=getchar();
  8. while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
  9. while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
  10. return fx?ret:-ret;
  11. }
  12. const int N=,M=5e5+,Tm=6e7;
  13. int n,m,hd[N],xnt,to[M<<],nxt[M<<];
  14. int cnt,col[N];bool a[N],b[N][N];
  15. void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
  16. void dfs(int cr)
  17. {
  18. col[cr]=cnt;
  19. for(int i=hd[cr],v;i;i=nxt[i])
  20. if(!col[v=to[i]])dfs(v);
  21. }
  22. void solve()
  23. {
  24. for(int i=;i<=n;i++)
  25. if(!col[i]) cnt++,dfs(i);
  26. for(int i=;i<=n;i++)b[i][i]=;
  27. for(int i=;i<=n;i++)
  28. for(int j=hd[i],v;j;j=nxt[j])
  29. if(a[i]==a[v=to[j]])b[i][v]=b[v][i]=;
  30. int pl=n*n;
  31. //for(int lj=0,cd=0;lj<=Tm&&cd<=n;lj+=pl,cd++)
  32. for(int lj=;lj<=Tm*;lj+=pl)
  33. {
  34. bool flag=;
  35. for(int i=;i<=n;i++)
  36. for(int j=i+;j<=n;j++)
  37. {
  38. if(b[i][j]||a[i]!=a[j]||col[i]!=col[j])continue;
  39. bool fg=;
  40. for(int l0=hd[i];l0&&!fg;l0=nxt[l0])
  41. for(int l1=hd[j];l1;l1=nxt[l1])
  42. if(b[to[l0]][to[l1]]){fg=;break;}
  43. if(fg)b[i][j]=b[j][i]=;
  44. else flag=;
  45. }
  46. if(!flag)break;
  47. }
  48. }
  49. int main()
  50. {
  51. n=rdn();m=rdn();int Q=rdn();
  52. char ch[N]; scanf("%s",ch+);
  53. for(int i=;i<=n;i++)a[i]=ch[i]-'';
  54. for(int i=,u,v;i<=m;i++)
  55. {
  56. u=rdn();v=rdn();add(u,v);add(v,u);
  57. }
  58. solve();int u,v;
  59. while(Q--)
  60. {
  61. u=rdn();v=rdn();puts(b[u][v]?"YES":"NO");
  62. }
  63. return ;
  64. }

30分暴力是这样:不是遍历不合法点对,而是遍历合法点对。

因为一个点对合法之后就没用变化,可以不用管了,所以在合法的时候把它的影响也算过,再不用管它,正确性和时间都是对的。遍历不合法点对,可能有很多失败尝试,时间没有保证。

即把合法点对压入队列,每次从队列里取出,遍历两个点出边看能否产生新的合法点对。因为点对合法之后不会有变化,所以遍历的先后之类的没有影响。

这样是 m2 的。

然后考虑把图的规模缩小。

因为发现有 “在一条边上来回走” 之类的情况,所以很多边去掉也不会影响答案。

然后从连接同色点和连接异色点的边来考虑。因为同色点之间可以来回走得到特定长度,异色点之间可以得到特定次数的颜色切换。把一个合法回文串拆成这两个部分考虑。

考虑所有连 0 类点的边构成的某个连通块。如果是二分图,则一个点到另一个点的长度任意,但一定是奇数长度或偶数长度中的一种。

  如果把该连通块删边至剩下一棵树,性质不会改变。两个点之间还是任意长度、奇数或偶数中的一种。

  可能本来可以较短地走过去,变成树之后不得不走很长才能走过去。不过在答案中只要在回文的另一侧多走一些就行了。

如果不是二分图,一个点到另一个点之间的长度和奇偶性都是任意的。只要在删成一棵树之后给某个点连一个自环就能让树等价于原图了。

连 1 类点的边也是一样。连异色点的边也是一样。不过连异色点的边构成的不会不是二分图。

然后图被删得剩下 O(n) 条边。刚才的做法就变成 n2 而可过了。

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. int rdn()
  6. {
  7. int ret=;bool fx=;char ch=getchar();
  8. while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
  9. while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
  10. return fx?ret:-ret;
  11. }
  12. const int N=,M=5e5+;
  13. int n,m,top,hd[N],xnt,to[M<<],nxt[M<<],fa[N];
  14. bool a[N],vis[N],col[N],b[N][N],flag;
  15. struct Ed{
  16. int x,y;
  17. Ed(int x=,int y=):x(x),y(y) {}
  18. }ed[M],sta[M];
  19. namespace G{
  20. int hd[N],xnt,to[N<<],nxt[N<<],q[N*N][];
  21. void add(int x,int y)
  22. {
  23. to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;
  24. to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;
  25. }
  26. void solve()
  27. {
  28. int he=, tl=;
  29. for(int i=;i<=n;i++)
  30. {
  31. q[++tl][]=i;q[tl][]=i;b[i][i]=;
  32. }
  33. for(int i=;i<=n;i++)
  34. for(int j=hd[i],v;j;j=nxt[j])
  35. if(a[v=to[j]]==a[i]&&i<v)
  36. {
  37. q[++tl][]=i;q[tl][]=v;b[i][v]=b[v][i]=;
  38. }
  39. while(he<tl)
  40. {
  41. int x=q[++he][], y=q[he][];
  42. for(int i=hd[x],v1;i;i=nxt[i])
  43. for(int j=hd[y],v2;j;j=nxt[j])
  44. if(a[v1=to[i]]==a[v2=to[j]]&&!b[v1][v2])//!b[][]
  45. {
  46. q[++tl][]=v1;q[tl][]=v2;b[v1][v2]=b[v2][v1]=;
  47. }
  48. }
  49. }
  50. }
  51. void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
  52. int fnd(int a){if(fa[a]==a)return a;return fa[a]=fnd(fa[a]);}
  53. void dfs(int cr)
  54. {
  55. vis[cr]=;
  56. for(int i=hd[cr],v;i;i=nxt[i])
  57. if(a[v=to[i]]==a[cr])
  58. {
  59. sta[++top]=Ed(cr,v);
  60. if(!vis[v])col[v]=!col[cr],dfs(v);
  61. else if(col[v]==col[cr])flag=;
  62. }
  63. }
  64. void init()
  65. {
  66. for(int i=;i<=n;i++)fa[i]=i;
  67. for(int i=;i<=n;i++)
  68. if(!a[i]&&!vis[i])
  69. {
  70. flag=;top=;dfs(i);if(flag)G::add(i,i);
  71. for(int j=,u,v;j<=top;j++)
  72. if((u=fnd(sta[j].x))!=(v=fnd(sta[j].y)))
  73. G::add(sta[j].x,sta[j].y), fa[u]=v;
  74. }
  75. for(int i=;i<=n;i++)
  76. if(a[i]&&!vis[i])
  77. {
  78. flag=;top=;dfs(i);if(flag)G::add(i,i);
  79. for(int j=,u,v;j<=top;j++)
  80. if((u=fnd(sta[j].x))!=(v=fnd(sta[j].y)))
  81. G::add(sta[j].x,sta[j].y), fa[u]=v;
  82. }
  83. for(int i=;i<=n;i++)fa[i]=i;//
  84. for(int i=,u,v;i<=m;i++)
  85. if((u=fnd(ed[i].x))!=(v=fnd(ed[i].y)))
  86. G::add(ed[i].x,ed[i].y), fa[u]=v;
  87. }
  88. int main()
  89. {
  90. n=rdn();int tp=rdn();int Q=rdn();
  91. char ch[N]; scanf("%s",ch+);
  92. for(int i=;i<=n;i++)a[i]=ch[i]-'';
  93. for(int i=,u,v;i<=tp;i++)
  94. {
  95. u=rdn();v=rdn();add(u,v);add(v,u);
  96. if(a[u]!=a[v])ed[++m]=Ed(u,v);
  97. }
  98. init(); G::solve(); int u,v;
  99. while(Q--)
  100. {
  101. u=rdn();v=rdn();puts(b[u][v]?"YES":"NO");
  102. }
  103. return ;
  104. }

LOJ 3057 「HNOI2019」校园旅行——BFS+图等价转化的更多相关文章

  1. Loj #3057. 「HNOI2019」校园旅行

    Loj #3057. 「HNOI2019」校园旅行 某学校的每个建筑都有一个独特的编号.一天你在校园里无聊,决定在校园内随意地漫步. 你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你 ...

  2. 「loj3057」「hnoi2019」校园旅行

    题目 一个n个点m条边的无向图,每个点有0 / 1 的标号; 有q个询问,每次询问(u,v)直接是否存在回文路径(可以经过重复的点和边); $1 \le n \le 5 \times 10^3  , ...

  3. Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...

  4. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

  5. Loj #3055. 「HNOI2019」JOJO

    Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...

  6. Loj 3058. 「HNOI2019」白兔之舞

    Loj 3058. 「HNOI2019」白兔之舞 题目描述 有一张顶点数为 \((L+1)\times n\) 的有向图.这张图的每个顶点由一个二元组 \((u,v)\) 表示 \((0\le u\l ...

  7. LOJ 3059 「HNOI2019」序列——贪心与前后缀的思路+线段树上二分

    题目:https://loj.ac/problem/3059 一段 A 选一个 B 的话, B 是这段 A 的平均值.因为 \( \sum (A_i-B)^2 = \sum A_i^2 - 2*B \ ...

  8. LOJ 3056 「HNOI2019」多边形——模型转化+树形DP

    题目:https://loj.ac/problem/3056 只会写暴搜.用哈希记忆化之类的. #include<cstdio> #include<cstring> #incl ...

  9. LOJ 3055 「HNOI2019」JOJO—— kmp自动机+主席树

    题目:https://loj.ac/problem/3055 先写了暴力.本来想的是 n<=300 的那个在树上暴力维护好整个字符串, x=1 的那个用主席树维护好字符串和 nxt 数组.但 x ...

随机推荐

  1. 前端UI框架选择区别对比推荐

    UI选择务必慎重,货比三家. 弱水三千只取一瓢:弱水三千只取一瓢,源起佛经中的一则故事,警醒人们在一生中可能会遇到很多美好的东西,但只要用心好好把握住其中的一样就足够了 老牌构建于jQuery框架之上 ...

  2. 20175317 《Java程序设计》第五周学习总结

    20175317 <Java程序设计>第五周学习总结 教材学习内容总结 第五周我学习了教材第六章的内容,了解了接口的知识,学到了以下内容: 明白了什么是接口 学会了如何实现接口 了解了接口 ...

  3. 虚拟环境之virtualenvwrapper

    原来的virtualenv工具使用特别麻烦,主要体现在以下几点 1 创建虚拟环境的命令太长,太难记 2 管理特别麻烦 3 进入虚拟环境需要找到这个虚拟环境的存放目录才行,如果没有统一的存放目录,很难找 ...

  4. sparse_tensor feed_dict的时候十分不方便。

    假如说,你再处理文本的时候,写tfrecord的时候用的变长的类型, example = tf.train.Example(features=tf.train.Features(feature={ ' ...

  5. 使用nginx作为webservice接口代理

    通常情况下,企业并不会直接开放系统接口给到外网,并且在企业内部同样有SOA或者ESB这样的接口统一管理的工具. 那么,大多数情况下,如果需要与外部系统,如云系统,或者其他企业的系统做接口时采取的方式如 ...

  6. 百度OCR

    注意点:  图像数据, base64编码后,记得要urlencode, 否则会提示image format wrong winform 多窗体相互切换form public partial class ...

  7. 浅谈对象的两个方法:Object.keys() ,Object.assign();

    1 : Object.keys(obj) 返回给定对象的所有可枚举属性的字符串数组 例子1: var arr = [1, 2, 6, 20, 1]; console.log(Object.keys(a ...

  8. day35-python 操作memcache二

    Memcache常用命令 存储命令: set/add/replace/append/prepend/cas 获取命令: get/gets 其他命令: delete/stats.. add方法 添加一条 ...

  9. .net 表达式返回值和等号赋值的区别

    .net 7.0的新特性中,有一个使用表达式体返回值的操作.请看如下代码: private string _userName=""; public string UserName{ ...

  10. 开始Flask项目

    新建Flask项目. 设置调试模式. 理解Flask项目主程序. 使用装饰器,设置路径与函数之间的关系. 使用Flask中render_template,用不同的路径,返回首页.登录员.注册页. 用视 ...