[AGC058C] Planar Tree 题解

前言

赛时没做出来,赛后把题补了。果然是 maroonrk 出的,名不虚传啊……真的很好的一道题目。

解法

题目中的圆周有以下几个性质:

  • 圆周上如果有相邻的等值,我们可以去掉一个而不改变答案(这个很好证明);
  • 如果 12 相邻,那么擦去 1 不影响答案;同样的道理,如果 34 相邻,擦去 4 不影响答案。

我们定义“规范化”为尽可能地多执行以上操作的过程。

任何满足要求的树也都具有满足以下条件的边:

  • 边连接的两个点在圆周上相邻;
  • 其中一个点是树的叶子。

所以,我们可以通过执行以下操作来形成满足条件的树:

  • 选择两个相邻且可连接的点并连接它们;
  • 选择其中一个点作为叶子,将它从圆周上擦除。

那么,该如何利用上述操作来简化题目呢?

我们可以发现,在“规范化”后的圆周上,我们只有可能连接编号为 23 的点。假设我们将 2 当作那片被擦掉的叶子:

  • 如果圆周上有这样的一段“弧”:,4,2,3,,擦除了 2 之后 34 相邻,就可以进行“规范化”擦除,间接地相当于擦除了一对 24
  • 如果圆周上有这样的: ,3,2,3,,那么按照上面的思路 23 也将被擦除;
  • ……

考虑以下一系列操作:在“规范化”状态下执行上述操作,然后再次“规范化”……

如果我们也考虑把 3 当作叶子(擦除 3),这一系列操作每次可以擦除一对 (2,3),(1,3)(2,4)

如果可以重复上面的操作,且最终只有 23 在圆周上,我们就可以构造出满足条件的树。

所以,我们可以推出以下必要条件:

  • 设圆环上 i 的数量为 Ci,则 C2>C4C3>C1

我们可以看到,实际上它也是充分条件。因为如果有顶点 14 在圆上,那么我们可以擦除一对 (1,3)(2,4)

我们可以在 O(n) 的时间复杂度内解决这个问题。

实现

注意到,我的代码中有一个 f 数组,其中 f={2,2,3,3}。但是因为我在实现中为方便处理,将所有输入的数减去了 1,所以代码中 f={1,1,2,2}

它的作用是什么呢?判断相邻的两个点能否进行规范化操作!我们注意到,如果两个点编号为 i,jfi=fj 的必要条件为 i=j|ij|=1

所以,如果 fi=fj,假设 fi=i,就代表 i=2i=3,不管 j 是什么,擦掉 j 都是“规范化”过程中的合法操作。

最后再用 std::map 来统计每个数出现的数量,比较后输出即可。

放代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
  ios::sync_with_stdio(false);
  int t,f[4]={1,1,2,2}; cin>>t;
  while(t--){
    int n; cin>>n;
    vector<int> a;
    for(int i=0;i<n;i++){
      int x; cin>>x;
      if(x--;i&&f[x]==f[a.back()]){
        if(f[x]==x)a.back()=x;
      }
      else a.emplace_back(x);
    }
    if(f[a[0]]==f[a.back()]){
      if(f[a[0]]==a.back())a[0]=a.back();
      a.pop_back();
    }
    vector<int> m(4); for(int i:a)m[i]++;
    cout<<(m[2]>m[0]&&m[1]>m[3]?"Yes\n":"No\n");
  }
  return 0;
}
posted @   FFTotoro  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示