P3588 [POI2015]PUS(拓扑排序+线段树)

P3588 [POI2015]PUS

对于每个$(l,r,k)$,将$k$个位置向剩下$r-l-k+1$个位置连边,边权为$1$,这样就保证$k$个位置比剩下的大

先给所有位置填$1e9$保证最优

然后拓扑排序填数

填的数不在$[1,1e9]$内或者出现环,即为不合法

但是这样边数过多会超时

于是考虑线段树优化建图

把$n$个点建成线段树,每个节点向左右儿子连边,边权为0。

这样每次连一个区间$[l,r]$就只需要$log(r-l+1)$次

注意不合法情况要枚举完整

#include<iostream>
#include<cstdio>
using namespace std;
#define N 100005
#define M 6000005
int n,s,m,u,pos[N],h[N*6],L,R,no;
int p[N*6],w[N*6],id[N]; bool vis[N*6];
int Cnt,hd[N*6],nxt[M],ed[N*6],poi[M],val[M],in[N*6];
void adde(int x,int y,int v){
    nxt[ed[x]]=++Cnt; hd[x]=hd[x]?hd[x]:Cnt;
    ed[x]=Cnt; poi[Cnt]=y; val[Cnt]=v; ++in[y];
}
#define mid (l+r)/2
int build(int o,int l,int r){
    w[p[o]=++u]=1e9;
    if(l==r) return id[l]=u;
    adde(p[o],build(o<<1,l,mid),0);
    adde(p[o],build(o<<1|1,mid+1,r),0);
    return p[o];
}
void Add(int o,int l,int r,int x1,int x2,int k){
    if(x1<=l&&r<=x2){adde(k,p[o],0); return ;}
    if(x1<=mid) Add(o<<1,l,mid,x1,x2,k);
    if(x2>mid) Add(o<<1|1,mid+1,r,x1,x2,k);
}
void work(){
    int tt=0;
    for(int i=1;i<=u;++i) if(!in[i]) h[++R]=i;
    while(L!=R){
        if(L>=N) L=0;
        int x=h[++L]; ++tt;
        if(w[x]<1) no=1;//填的数<1
        for(int i=hd[x];i;i=nxt[i]){
            int to=poi[i];
            if(vis[to]&&w[to]>w[x]-val[i]) no=1;//填的数比已给定位置上的数值小
            w[to]=min(w[to],w[x]-val[i]);
            if((--in[to])==0){
                if(R>=N) R=0;
                h[++R]=to;
            }
        }
    }
    if(tt<u) no=1;//图中有环
}
int main(){
    scanf("%d%d%d",&n,&s,&m);
    build(1,1,n);
    for(int i=1,Id,v;i<=s;++i){
        scanf("%d%d",&Id,&v);
        if(v<1||v>1e9) no=1;//给定数不合法
        w[id[Id]]=v; vis[id[Id]]=1;
    }
    for(int i=1,l,r,k;i<=m;++i){
        scanf("%d%d%d",&l,&r,&k); w[++u]=1e9;//新建一个中转节点
        for(int j=1;j<=k;++j)
            scanf("%d",&pos[j]),adde(id[pos[j]],u,1);
        if(l<pos[1]) Add(1,1,n,l,pos[1]-1,u);
        if(r>pos[k]) Add(1,1,n,pos[k]+1,r,u);
        for(int j=1;j<k;++j)
            if(pos[j]+1<pos[j+1])
                Add(1,1,n,pos[j]+1,pos[j+1]-1,u);
    }work();
    if(no) puts("NIE");
    else{
        puts("TAK");
        for(int i=1;i<=n;++i) printf("%d ",w[id[i]]);
    }return 0;
} 

原文地址:https://www.cnblogs.com/kafuuchino/p/11729907.html

时间: 2024-11-05 18:28:12

P3588 [POI2015]PUS(拓扑排序+线段树)的相关文章

HDU 5195 DZY Loves Topological Sorting (拓扑排序+线段树)

题目地址:HDU 5195 简直受不了了..BC第二题都开始线段树+拓扑排序了... 这题很容易想到拓扑排序过程中贪心,但是贪心容易TLE,所以需要用数据结构去维护,我用的是线段树维护.每次找入度小于等于k的编号最大的点,这样就可以保证字典序一定是最大的. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorith

计数排序 + 线段树优化 --- Codeforces 558E : A Simple Task

E. A Simple Task Problem's Link: http://codeforces.com/problemset/problem/558/E Mean: 给定一个字符串,有q次操作,每次操作将(l,r)内的字符升序或降序排列,输出q次操作后的字符串. analyse: 基本思想是计数排序. 所谓计数排序,是对一个元素分布较集中的数字集群进行排序的算法,时间复杂度为O(n),但使用条件很苛刻.首先对n个数扫一遍,映射出每个数字出现的次数,然后再O(n)扫一遍处理出:对于数字ai,

拓扑排序,树的直径模板(CF14D 枚举删边)

HDU4607 树的直径 #include <stdio.h> #include <string.h> #include <iostream> #include <queue> #include <vector> using namespace std; #define N 100005 #define INF 1<<30 int n,dis[N],E; bool vis[N]; vector<int>G[N]; //注意

CF 558E(A Simple Task-计数排序+线段树)

E. A Simple Task time limit per test 5 seconds memory limit per test 512 megabytes input standard input output standard output This task is very simple. Given a string S of length n and q queries each query is on the format i j k which means sort the

Codeforces 558E A Simple Task (计数排序&amp;&amp;线段树优化)

题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memory limit per test512 megabytes inputstandard input outputstandard output This task is very simple. Given a string S of length n and q queries each quer

[codeforces1284D]New Year and Conference 离散化+multiset【或排序+线段树】

[题目]:题目链接 time limit per test 2 seconds memory limit per test 1024 megabytes input standard input output standard output Filled with optimism, Hyunuk will host a conference about how great this new year will be! The conference will have nn lectures.

Codeforces Round #625 Div1 C,二维偏序,排序+线段树

题目 题意: 有若干武器A,攻击力A1,费用A2, 有若干铠甲B,防御力B1,费用B2, 有若干怪兽M,攻击力M1,防御力M2,奖励M3 你可以选择一把武器,一个铠甲,打败所有攻击和防御都严格小的怪兽,问最大收益. 思路: 典型的二维偏序问题,把攻击和防御想象成二维的坐标轴,我们要找到的其实就是一个矩形里(0,0)~(x,y)里收益-这个矩形的花费的最大值.我们可以排序一维,另一维用线段树维护,枚举的时候往里面加怪兽就好. 代码: #include <bits/stdc++.h> using

洛谷P3588 - [POI2015]Pustynia

Portal Description 给定一个长度为\(n(n\leq10^5)\)的正整数序列\(\{a_n\}\),每个数都在\([1,10^9]\)范围内,告诉你其中\(s\)个数,并给出\(m(m\leq2\times10^5)\)条信息.每条信息包含三个数\(L,R,k(Σk\leq 3\times10^5)\)以及\(k\)个正整数\(\{x_k\}\),表示\(a_L..a_R\)中,任意一个\(x\)均比剩下的\(R-L+1-k\)个数大(严格大于,即没有等号).请任意构造出一组

POJ 1436 Horizontally Visible Segments (线段树&amp;#183;区间染色)

题意   在坐标系中有n条平行于y轴的线段  当一条线段与还有一条线段之间能够连一条平行与x轴的线不与其他线段相交  就视为它们是可见的  问有多少组三条线段两两相互可见 先把全部线段存下来  并按x坐标排序  线段树记录相应区间从右往左当前可见的线段编号(1...n)  超过一条就为0  然后从左往右对每条线段  先查询左边哪些线段和它是可见的  把可见关系存到数组中  然后把这条线段相应区间的最右端可见编号更新为这条线段的编号  最后暴力统计有多少组即可了 #include <cstdio>