• [NWRRC2015] Insider's Information


    一、题目

    点此看题

    二、解法

    我们从两侧往中间插入,那么三元组 \((a,b,c)\) 合法的必要条件是:\(a\)\(c\) 先于 \(b\) 插入。

    发现我们可以很轻易地让所有三元组都满足必要条件,方法是连边 \(a\rightarrow b\)\(c\rightarrow b\)但是只让 \(b\) 的入度增加 \(1\) ,然后跑拓扑排序,只要按照拓扑序插入即可保证 \(a\)\(c\) 先于 \(b\) 插入。由于题目条件 保证存在一个排列满足所有限制,可知任何时刻都会有入度为 \(0\) 的点,那么一定也存在这样的拓扑序。

    下一步是依照拓扑序,决定每个点是放最左边还是放在最右边。考虑三元组 \((a,b,c)\) 在拓扑序上可能的顺序有:(a,b,c),(a,c,b),(c,b,a),(c,a,b),由于 \(a,c\) 是对称的,我们不妨只考虑 \(a\) 已经被放在最左边的情况:

    • 若顺序是 (a,b,c),那么产生贡献的充要条件是 \(b\) 被放在最左边。
    • 若顺序是 (a,c,b),那么产生贡献的充要条件是 \(c\) 被放在最右边。

    这说明:我们只需要在将要加入第二个元素的时候考虑这个限制。实现时把拓扑排序和决定位置同时进行,假设现在要插入点 \(u\),我们考虑 \(u\) 作为 \(b\) 的限制(对应的 \(a\) 已经被插入),和 \(u\) 作为 \(c\) 的限制(对应的 \(a\) 已经被插入),可以统计 \(u\) 放在最左边的贡献和 \(u\) 放在最右边的贡献,去较大的那边放置即可。

    时间复杂度 \(O(n)\),由于每次都是少数服从多数,被满足的限制至少有 \(\lceil\frac{m}{2}\rceil\) 个。

    三、总结

    我觉得本题的最大亮点就是把限制放在单点上,为达成这一目的,我们可能需要先拆解限制(比如本题一开始的必要条件),然后在若干先决条件的基础上可以在单点上进行决策。类似精妙拆限制的题还有:JOISC 2015 Day2 Keys

    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int M = 100005;
    #define pb push_back
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,a[M],b[M],c[M],d[M],p[M],ans[M];
    vector<int> g[M],s[M];queue<int> q;
    signed main()
    {
    	n=read();m=read();
    	for(int i=1;i<=m;i++)
    	{
    		a[i]=read();b[i]=read();c[i]=read();
    		d[b[i]]++;s[b[i]].pb(i);
    		g[a[i]].pb(i);g[c[i]].pb(i);
    	}
    	for(int i=1;i<=n;i++)
    		if(!d[i]) q.push(i);
    	int l=1,r=n;
    	while(!q.empty())
    	{
    		int u=q.front(),w[2]={};q.pop();
    		for(int v:g[u])
    		{
    			if(p[a[v]] || p[c[v]])
    				w[p[u^a[v]^c[v]]<l]++;
    			else if(!--d[b[v]]) q.push(b[v]);
    		}
    		for(int v:s[u])
    			if(!(p[a[v]] && p[c[v]]))
    				w[(p[a[v]]^p[c[v]])>r]++;
    		p[u]=w[0]<w[1]?r--:l++;
    	}
    	for(int i=1;i<=n;i++) ans[p[i]]=i;
    	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    	puts("");
    }
    
  • 相关阅读:
    mysql视图产生派生表无法优化案例
    根据.frm .ibd文件恢复表
    binlog内容时间乱序问题排查
    mysql官方的测试数据库employees超30万的数据,安装方法介绍
    数据库大量Waiting for table flush 状态SQL问题排查
    mysql搭建从库并配置ssl
    MySQL lOAD DATA详解
    redis eval
    aws-rds for mysql 5.7.34时间点恢复数据
    MySQL 如何处理监听连接的
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16358646.html
Copyright © 2020-2023  润新知