zoukankan      html  css  js  c++  java
  • 【bzoj4383】[POI2015]Pustynia 线段树优化建图+差分约束系统+拓扑排序

    题目描述

    给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l],a[l+1],...,a[r-1],a[r]里这k个数中的任意一个都比任意一个剩下的r-l+1-k个数大(严格大于,即没有等号)。
    请任意构造出一组满足条件的方案,或者判断无解。

    输入

    第一行包含三个正整数n,s,m(1<=s<=n<=100000,1<=m<=200000)。
    接下来s行,每行包含两个正整数p[i],d[i](1<=p[i]<=n,1<=d[i]<=10^9),表示已知a[p[i]]=d[i],保证p[i]递增。
    接下来m行,每行一开始为三个正整数l[i],r[i],k[i](1<=l[i]<r[i]<=n,1<=k[i]<=r[i]-l[i]),接下来k[i]个正整数x[1],x[2],...,x[k[i]](l[i]<=x[1]<x[2]<...<x[k[i]]<=r[i]),表示这k[i]个数中的任意一个都比任意一个剩下的r[i]-l[i]+1-k[i]个数大。Σk <= 300,000

    输出

    若无解,则输出NIE。
    否则第一行输出TAK,第二行输出n个正整数,依次输出序列a中每个数。

    样例输入

    5 2 2
    2 7
    5 3
    1 4 2 2 3
    4 5 1 4

    样例输出

    TAK
    1 7 5 4 3


    题解

    线段树优化建图+差分约束系统+拓扑排序

    先想朴素的建图方法:利用差分约束的思想,如果要求x比y大,那么连边y->x,长度为1。对于每条信息,将非给定节点向一个新建节点连边,长度为0;新建节点向所有给定节点连边,长度为1。然后拓扑排序,如果有点没被更新,或者与给定的点权冲突(即要求比给出的大)则无解,否则出解。

    这样建图边数爆炸。考虑到每次给出的都是一段区间,所以可以使用线段树优化这个建图过程。

    建立线段树,从子节点到父节点连长度为0的边;对于每条信息,把非给定节点分成k+1个区间,每个区间在线段树中找到对应位置,这些位置向新建节点连边,长度为0;新建节点向给定节点对应的叶子结点连边,长度为1。然后拓扑排序即可。

    至于拓扑排序求解的方法,需要记录f数组和t数组,分别表示点的权值和点的已知权值,如果遇到一个点存在已知权值,且f>t,则无解,否则f取f与t的max。

    另外,“每个数都在1到10^9范围内”这个10^9是有用的,当f值大于10^9时也应算作无解。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define N 1000010
    #define M 3000010
    #define lson l , mid , x << 1
    #define rson mid + 1 , r , x << 1 | 1
    using namespace std;
    queue<int> q;
    int head[N] , to[M] , len[M] , next[M] , cnt = 1 , tot , p[N] , f[N] , t[N] , x[N] , rd[N];
    void add(int x , int y , int z)
    {
    	to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt , rd[y] ++ ;
    }
    void build(int l , int r , int x)
    {
    	if(l == r)
    	{
    		p[l] = x;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(lson) , build(rson);
    	add(x << 1 , x , 0) , add(x << 1 | 1 , x , 0);
    }
    void update(int b , int e , int t , int l , int r , int x)
    {
    	if(b <= l && r <= e)
    	{
    		add(x , t , 0);
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(b <= mid) update(b , e , t , lson);
    	if(e > mid) update(b , e , t , rson);
    }
    int main()
    {
    	int n , s , m , i , u , c , d , l , r , k;
    	scanf("%d%d%d" , &n , &s , &m);
    	build(1 , n , 1) , tot = 4 * n;
    	while(s -- ) scanf("%d%d" , &c , &d) , f[p[c]] = t[p[c]] = d;
    	while(m -- )
    	{
    		scanf("%d%d%d" , &l , &r , &k) , x[0] = l - 1 , x[k + 1] = r + 1 , tot ++ ;
    		for(i = 1 ; i <= k ; i ++ ) scanf("%d" , &x[i]) , add(tot , p[x[i]] , 1);
    		for(i = 0 ; i <= k ; i ++ ) if(x[i + 1] - x[i] > 1) update(x[i] + 1 , x[i + 1] - 1 , tot , 1 , n , 1);
    	}
    	for(i = 1 ; i <= tot ; i ++ )
    		if(!rd[i])
    			f[i] = max(f[i] , 1) , q.push(i);
    	while(!q.empty())
    	{
    		u = q.front() , q.pop();
    		for(i = head[u] ; i ; i = next[i])
    		{
    			f[to[i]] = max(f[to[i]] , f[u] + len[i]) , rd[to[i]] -- ;
    			if(t[to[i]] && f[to[i]] > t[to[i]])
    			{
    				printf("NIE
    ");
    				return 0;
    			}
    			if(!rd[to[i]]) q.push(to[i]);
    		}
    	}
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		if(!f[p[i]] || f[p[i]] > 1000000000)
    		{
    			printf("NIE
    ");
    			return 0;
    		}
    	}
    	printf("TAK
    ");
    	for(i = 1 ; i < n ; i ++ ) printf("%d " , f[p[i]]);
    	printf("%d" , f[p[n]]);
    	return 0;
    }
    

     

  • 相关阅读:
    七、文件的排序、合并和分割
    六、awk编程
    五、sed命令
    四、grep命令
    三、正则表达式
    二、Linux文件系统和文本编辑器
    一、shell基础知识点
    mysql实现交易编码生成(代替oracle的序列)
    新安装Centos无法访问网络
    uiview 动画
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7115018.html
Copyright © 2011-2022 走看看