P r o b l e m \mathrm{Problem} Problem
给定一个长度为 n n n的正整数序列 a a a,每个数都在 1 1 1到 1 0 9 10^9 109范围内,告诉你其中 s s s个数,并给出 m m m条信息,每条信息包含三个数 l , r , k l,r,k l,r,k以及接下来 k k k个正整数,表示 a [ l ] , a [ l + 1 ] , … , a [ r − 1 ] , a [ r ] a[l],a[l+1],…,a[r-1],a[r] a[l],a[l+1],…,a[r−1],a[r]里这 k k k个数中的任意一个都比任意一个剩下的 r − l + 1 − k r-l+1-k r−l+1−k个数大(严格大于,即没有等号)。请任意构造出一组满足条件的方案,或者判断无解。
第一行包含三个正整数 n , s , m n,s,m n,s,m。接下来 s s s行,每行包含两个正整数 p [ i ] , d [ i ] p[i],d[i] p[i],d[i],表示已知 a [ p [ i ] ] = d [ i ] a[p[i]]=d[i] a[p[i]]=d[i],保证 p [ i ] p[i] p[i]递增。接下来 m m m行,每行一开始为三个正整数 l [ i ] , r [ i ] l[i],r[i] l[i],r[i]
k [ i ] ( 1 ≤ l [ i ] ≤ r [ i ] ≤ n , 1 ≤ k [ i ] ≤ r [ i ] − l [ i ] ) k[i](1≤l[i]\le r[i]≤n,1≤k[i]≤r[i]-l[i]) k[i](1≤l[i]≤r[i]≤n,1≤k[i]≤r[i]−l[i]),接下来 k [ 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]\le x[2]\le …\le 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] k[i]个数中的任意一个都比任意一个剩下的 r [ i ] − l [ i ] + 1 − k [ i ] r[i]-l[i]+1-k[i] r[i]−l[i]+1−k[i]个数大。
对于100%的数据, 1 ≤ s ≤ n ≤ 100000 , 1 ≤ m ≤ 200000 , Σ k ≤ 300 , 000 1≤s≤n≤100000,1≤m≤200000,Σk≤300,000 1≤s≤n≤100000,1≤m≤200000,Σk≤300,000。
S o l u t i o n \mathrm{Solution} Solution
观察到数据范围, ∑ k ≤ 3 × 1 0 5 \sum k\le 3\times 10^5 ∑k≤3×105,我们需要考虑一个 O ( k ) O(k) O(k)的算法。
我们知道,我们需要用一个很好的算法来确立大小关系,可以用大数向小数连边,再用拓扑徐进行赋值即可。但这道题目的瓶颈在于,连的边太多了,复杂度不允许。怎么办呢?
- 我们观察到向一个区间连边,我们可以使用线段树优化建图的方式。
- 实质上很简单,就是我们纯粹的向线段树上对应的区间最上面一层的点连边即可。
Just like this, p p p就是线段树对应着一个区间的节点,这样我们就成功完成了建图。
考虑如何进行赋值,我们设当前数值为 f x f_x fx且存在边 ( x , y , v ) (x,y,v) (x,y,v).
- 若 y y y为定值且 f x − v < a y f_x-v<a_y fx−v<ay,则无解。
- 否则令 f y = min ( f x − v ) f_y=\min(f_x-v) fy=min(fx−v)。
- 由于 f f f数组处于不断递减的状态,则我们令所有入度为 0 0 0的点 f i = 0 f_i=0 fi=0.
- 无解的判定:存在 f i < 1 o r f i > 1 0 9 f_i<1\mathrm{\ \ or\ \ }f_i>10^9 fi<1 or fi>109。
C o d e \mathrm{Code} Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6;
int n, cnt(0), s, m, root;
int L[N], R[N], in[N], f[N], lc[N], rc[N], Num[N], pos[N], vis[N];
vector < pair < int, int > > a[N];
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s * 10 + c - 48, c = getchar();
return w ? -s : s;
}
void build(int &p,int l,int r)
{
if (l == r) {
p = l;
L[p] = l, R[p] = r;
return;
}
p = ++ cnt;
L[p] = l, R[p] = r;
int mid = l + r >> 1;
build(lc[p],l,mid);
build(rc[p],mid+1,r);
a[p].push_back({lc[p],0}), in[lc[p]] ++;
a[p].push_back({rc[p],0}), in[rc[p]] ++;
}
void MakeE(int &p,int l,int r,int x)
{
if (l <= L[p] && R[p] <= r) {
a[x].push_back({p,1});
in[p] ++;
return;
}
int mid = L[p] + R[p] >> 1;
if (l <= mid) MakeE(lc[p],l,r,x);
if (r > mid) MakeE(rc[p],l,r,x);
}
void TopSort(void)
{
queue < int > q;
for (int i=1;i<=cnt;++i)
{
if (in[i] == 0) q.push(i);
if (Num[i]) f[i] = Num[i];
else f[i] = 1e9;
}
while (q.size())
{
int x = q.front(); q.pop();
vis[x] = 1;
for (int i=0;i<a[x].size();++i)
{
int y = a[x][i].first;
int v = a[x][i].second;
in[y] --;
if (Num[y] && f[x] - v < f[y])
return puts("NIE"), void();
f[y] = min(f[y],f[x]-v);
if (f[y] < 1) return puts("NIE"), void();
if (in[y] == 0) q.push(y);
}
}
for (int i=1;i<=n;++i)
if (vis[i] == 0) return puts("NIE"), void();
puts("TAK");
for (int i=1;i<=n;++i) printf("%d ", f[i]);
}
int main(void)
{
n = cnt = read();
s = read(), m = read();
build(root,1,n);
for (int i=1;i<=s;++i) {
int x = read(), v = read();
Num[x] = v;
if (x < 1 || x > 1e9)
return puts("-1"), 0;
}
for (int i=1;i<=m;++i)
{
cnt ++;
int l = read(), r = read(), k = read();
for (int j=1;j<=k;++j) {
pos[j] = read();
a[pos[j]].push_back({cnt,0});
in[cnt] ++;
}
if (pos[1] > l) MakeE(root,l,pos[1]-1,cnt);
if (pos[k] < r) MakeE(root,pos[k]+1,r,cnt);
for (int i=2;i<=k;++i)
if (pos[i-1] + 1 < pos[i])
MakeE(root,pos[i-1]+1,pos[i]-1,cnt);
}
TopSort();
return 0;
}