NWRRC2015 Insider’s Information
NWRRC2015 Insider’s Information
题意
对于 的排列,给 个限制,第 个限制描述 出现在 , 之间。保证存在一个排列满足所有限制。
构造一个 的排列至少满足 个限制。
分析
每个限制连接两条有向边 ,,因为所有限制都可以被满足,所以在原序列中的端点一定入度为 。
这个图虽然存在环,但是我们对这个图进行拓扑排序,一次删除一个约束的两条边,仍然是可以将所有点入队出队的,但是排序后仅能保证其中每个约束中, 点不会同时排在 , 前面。但是这已经足够做此题了。
证明很简单,如果一个 的度变成 ,一定是它的所有入边都被删除了,所以不存在 被删除,, 都仍未删除的情况。
我们按拓扑序考虑将点加入最左边还是最右边,从两边往中间堆元素,直到填满排列为止。
对于一个约束,第一个放入的如果是 ,则无论 , 怎么放,都不可能满足约束,而我们的拓扑排序不存在 第一个放的情况。
如果第一个放的是 或 ,第二个放的点的位置就决定了该约束是否能满足。
如果第二个放的是 ,那么它放在第一个放的元素同侧,则最后一个元素无论怎么放,约束都满足。如果放在异侧,则无论最后一个元素怎么放,约束都不满足。
如果第二个放的是 或 那么第三个放的一定是 ,所以第一个和第二个放的 , 只要在异侧,约束一定满足,在同侧则一定不满足。
我们对于每个枚举的元素,统计它属于的约束中,有多少次是第二个放的元素,然后统计这些情况中,是放在左端满足的约束多还是放在右端满足的约束多,放到满足约束多的一侧,这样就保证了一定满足 个限制。
代码实现
因为每个约束会给 带来两个入度,而拓扑排序时我们需要给 减去两个入度,所以为了方便,不如直接用一个入度表示一个约束。
过程中我们一遍拓扑排序一边处理,因为每个点的位置只和已经放入排列中的点有关,所以这样处理也是正确的。
实现还是很短的,指针轻喷。
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)