NWRRC2015 Insider’s Information

NWRRC2015 Insider’s Information

题意

对于 [1,n] 的排列,给 m 个限制,第 i 个限制描述 bi 出现在 aici 之间。保证存在一个排列满足所有限制。

构造一个 n 的排列至少满足 m2 个限制。

分析

每个限制连接两条有向边 (aibi)(cibi),因为所有限制都可以被满足,所以在原序列中的端点一定入度为 0

这个图虽然存在环,但是我们对这个图进行拓扑排序,一次删除一个约束的两条边,仍然是可以将所有点入队出队的,但是排序后仅能保证其中每个约束中,b 点不会同时排在 ac 前面。但是这已经足够做此题了。

证明很简单,如果一个 b 的度变成 0,一定是它的所有入边都被删除了,所以不存在 b 被删除,ac 都仍未删除的情况。

我们按拓扑序考虑将点加入最左边还是最右边,从两边往中间堆元素,直到填满排列为止。

对于一个约束,第一个放入的如果是 b,则无论 ab 怎么放,都不可能满足约束,而我们的拓扑排序不存在 b 第一个放的情况。

如果第一个放的是 ac,第二个放的点的位置就决定了该约束是否能满足。

如果第二个放的是 b,那么它放在第一个放的元素同侧,则最后一个元素无论怎么放,约束都满足。如果放在异侧,则无论最后一个元素怎么放,约束都不满足。

如果第二个放的是 ac 那么第三个放的一定是 b,所以第一个和第二个放的 ac 只要在异侧,约束一定满足,在同侧则一定不满足。

我们对于每个枚举的元素,统计它属于的约束中,有多少次是第二个放的元素,然后统计这些情况中,是放在左端满足的约束多还是放在右端满足的约束多,放到满足约束多的一侧,这样就保证了一定满足 m2 个限制。

代码实现

因为每个约束会给 bi 带来两个入度,而拓扑排序时我们需要给 bi 减去两个入度,所以为了方便,不如直接用一个入度表示一个约束。

过程中我们一遍拓扑排序一边处理,因为每个点的位置只和已经放入排列中的点有关,所以这样处理也是正确的。

实现还是很短的,指针轻喷。

unsigned m, n, M;
unsigned Top(0), Tl(0), Hd(0);
unsigned A, B, C, D, t;
unsigned Cnt(0), Ans[100005], Tmp(0), BdL(0), BdR;
struct Que;
struct Node {
  vector<Que*> Bel; 
  unsigned Deg;
  char Vis, Le;
}N[100005], * Queue[100005];
struct Que {
  Node* Le, * Ri, * Mid;
  Que* Nxt;
}Q[100005];
signed main() {
  BdR = (n = RD()) + 1, m = RD();
  for (unsigned i(1); i <= m; ++i) {
    A = RD(), B = RD(), C = RD(), ++N[B].Deg;
    Q[i].Le = N + A, N[A].Bel.emplace_back(Q + i);
    Q[i].Mid = N + B, N[B].Bel.emplace_back(Q + i);
    Q[i].Ri = N + C, N[C].Bel.emplace_back(Q + i);
  }
  for (unsigned i(1); i <= n; ++i) if(!N[i].Deg) Queue[++Tl] = N + i;
  while (Tl ^ Hd) {//Topo and Solve 
    Node* Cur(Queue[++Hd]);
    unsigned PutLeft(100000);
    Cur->Vis = 1;
    for (auto Prs:Cur->Bel) {
      if(Cur == Prs->Le) {//Cur is A
        if(Prs->Ri->Vis) {//2nd or 3rd
          if(!(Prs->Mid->Vis)) {if(Prs->Ri->Le) --PutLeft; else ++PutLeft;}//2nd Add
        } else if(!(--(Prs->Mid->Deg))) Queue[++Tl] = Prs->Mid;//1st Add
      } else {//Cur is C or B
        if(Cur == Prs->Ri) {//Cur is C
          if(Prs->Le->Vis) {//2nd or 3rd
            if(!(Prs->Mid->Vis)) {if(Prs->Le->Le) --PutLeft; else ++PutLeft;}//2nd Add
          } else if(!(--(Prs->Mid->Deg)))Queue[++Tl] = Prs->Mid; //1st Add
        } else {//Cur is B
          if(!((Prs->Ri->Vis) & (Prs->Le->Vis))) {//2nd Add
            if(Prs->Ri->Vis) {if(Prs->Ri->Le) ++PutLeft;else --PutLeft;}//1st is C
            else {if(Prs->Le->Le) ++PutLeft;else --PutLeft;}//1st is A
          }
        }
      }
    }
    Cur->Le = (PutLeft >= 100000);
    Ans[(PutLeft < 100000) ? (--BdR) : (++BdL)] = Cur - N; 
  }
  for (unsigned i(1); i <= n; ++i) printf("%u ", Ans[i]);
  return Wild_Donkey;
}
posted @   Wild_Donkey  阅读(66)  评论(1编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示