[Luogu P3588] [BZOJ 4383] [POI2015] PUS - Pustynia

洛谷传送门
BZOJ传送门

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:
5 2 2
2 7
5 3
1 4 2 2 3
4 5 1 4
输出样例#1:
TAK
6 7 1000000000 6 3

解题分析

看这道题很容易想到建图连边拓扑排序, 再来判定是否有解。 但显然直接连边的复杂度是 O(N2) O ( N 2 ) 的, 显然过不去。注意到 k300000 ∑ k ≤ 300000 , 所以我们用线段树优化连边, 每个较小的区间连向一个虚点, 边权为0, 虚点再连向每个较大的点, 边权为1(表示至少大1), 最后跑一遍拓扑排序即可。

注意, 题目中的“所有元素都在 1109 1 ∼ 10 9 是有用的! 如果某个数超出了这个范围也要输出 NIE N I E

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 2000500
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define OK (lef >= lb && rig <= rb)
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int dot, arr, q, num, root, cnt;
int head[MX], deg[MX], val[MX], infer[MX], pos[MX], buf[MX];
std::queue <int> que;
struct Edge
{int to, len, nex;} edge[MX << 1];
IN void addedge(R int from, R int to, R int len)
{edge[++cnt] = {to, len, head[from]}, head[from] = cnt, ++deg[to];}
namespace SGT
{
    struct Node {int son[2];} tree[MX << 2];
    void build(int &now, R int lef, R int rig)
    {
        now = ++arr;
        if(lef == rig) return pos[lef] = now, void();
        int mid = lef + rig >> 1;
        build(ls, lef, mid);
        build(rs, mid + 1, rig);
        addedge(ls, now, 0), addedge(rs, now, 0);
    }
    void modify(R int now, R int lef, R int rig, R int lb, R int rb, R int tar)
    {
        if(OK) return addedge(now, tar, 0), void();
        int mid = lef + rig >> 1;
        if(lb <= mid) modify(ls, lef, mid, lb, rb, tar);
        if(rb > mid) modify(rs, mid + 1, rig, lb, rb, tar); 
    }
}
int main(void)
{
    int a, b, l, r, tt;
    in(dot), in(num), in(q);
    SGT::build(root, 1, dot);
    for (R int i = 1; i <= num; ++i)
    {
        in(a); in(b);
        val[pos[a]] = infer[pos[a]] = b;
    }
    for (R int i = 1; i <= q; ++i)
    {
        in(l), in(r), in(tt); ++arr; buf[0] = l - 1, buf[tt + 1] = r + 1;
        for (R int i = 1; i <= tt; ++i) in(buf[i]), addedge(arr, pos[buf[i]], 1);
        for (R int i = 0; i <= tt; ++i)
        if(buf[i] + 1 < buf[i + 1]) SGT::modify(1, 1, dot, buf[i] + 1, buf[i + 1] - 1, arr);
    }
    for (R int i = 1; i <= arr; ++i)
    {if(!deg[i]) infer[i] = std::max(1, infer[i]), que.push(i);}
    R int now;
    W (!que.empty())
    {
        now = que.front(); que.pop();
        for (R int i = head[now]; i; i = edge[i].nex)
        {
            infer[edge[i].to] = std::max(infer[edge[i].to], infer[now] + edge[i].len);
            if(val[edge[i].to] && infer[edge[i].to] > val[edge[i].to]) goto fail;
            if(!(--deg[edge[i].to])) que.push(edge[i].to);
        }
    }
    for (R int i = 1; i <= dot; ++i)
    if(infer[pos[i]] > 1000000000 || !infer[pos[i]]) goto fail;
    printf("TAK\n");
    for (R int i = 1; i <= dot; ++i) printf("%d ", infer[pos[i]]);
    return 0;
    fail: ;
    printf("NIE");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值