洛谷传送门
BZOJ传送门
题目描述
给定一个长度为 n n 的正整数序列,每个数都在 1 1 到范围内,告诉你其中 s s 个数,并给出条信息,每条信息包含三个数 l,r,k l , r , k 以及接下来 k k 个正整数,表示里这 k k 个数中的任意一个都比任意一个剩下的个数大(严格大于,即没有等号)。请任意构造出一组满足条件的方案,或者判断无解。
输入输出格式
输入格式:
第一行包含三个正整数 n,s,m(1≤s≤n≤100000,1≤m≤200000) n , s , m ( 1 ≤ s ≤ n ≤ 100000 , 1 ≤ m ≤ 200000 ) 。接下来 s s 行,每行包含两个正整数,表示已知 a[p[i]]=d[i] a [ p [ i ] ] = d [ i ] ,保证 p[i] p [ i ] 递增。接下来 m m 行,每行一开始为三个正整数,接下来 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]+1−k[i] r [ i ] − l [ i ] + 1 − k [ i ] 个数大。 Σk≤300,000 Σ k ≤ 300 , 000
输出格式:
若无解,则输出 NIE N I E 。否则第一行输出 TAK T A K ,第二行输出 n n 个正整数,依次输出序列中每个数。
输入输出样例
输入样例#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 ) 的, 显然过不去。注意到 ∑k≤300000 ∑ k ≤ 300000 , 所以我们用线段树优化连边, 每个较小的区间连向一个虚点, 边权为0, 虚点再连向每个较大的点, 边权为1(表示至少大1), 最后跑一遍拓扑排序即可。
注意, 题目中的“所有元素都在 1∼109 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");
}