4383: [POI2015]Pustynia

4383: [POI2015]Pustynia

Time Limit: 10 Sec   Memory Limit: 128 MBSec   Special Judge
Submit: 329   Solved: 119
[ Submit][ Status][ Discuss]

Description

给定一个长度为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个数大(严格大于,即没有等号)。
请任意构造出一组满足条件的方案,或者判断无解。

Input

第一行包含三个正整数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]Σk <= 300,000

Output

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

Sample Input

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

Sample Output

TAK
6 7 1000000000 6 3

HINT

Source

鸣谢Claris

[ Submit][ Status][ Discuss]

把>看成一条有向边,即如果a > b,那么就连一条a到b的边
假设每个点初始值为1E9,如果有特殊限制,那这个点初始值就是限制的值
每条有向边的权值都是1,代表走这条边权值一定要-1
如果有解,那么连出来的图肯定是个DAG,直接dp找最长路就行了
不过这样建图边数是O(n^2)的,肯定不行
把数组的每个点拿出,构成一棵线段树
对于每条约束,新建一个虚拟节点x,对于那k个数中,向x连权值为0的边
然后x就向线段树中对应的一个个区间连权值为1的边
因为∑k <= 3E5,所以总边数不超过O(klogn),这样就可以dp了
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
 
const int maxn = 1E5 + 10;
const int maxm = maxn * 10;
const int T = 4;
const int INF = 1000000000;
 
struct E{
    int to,w; E(){}
    E(int to,int w): to(to),w(w){}
};
 
int n,s,m,cnt,lc[maxn*T],rc[maxn*T],Num[maxn],A[maxm],du[maxm],B[maxn];
bool bo[maxm];
 
vector  v[maxm];
queue  Q;
 
void Build(int o,int l,int r)
{
    if (l == r) {Num[l] = o; return;}
    int mid = l + r >> 1;
    lc[o] = ++cnt; Build(lc[o],l,mid);
    rc[o] = ++cnt; Build(rc[o],mid+1,r);
    ++du[lc[o]]; ++du[rc[o]];
    v[o].push_back(E(lc[o],0));
    v[o].push_back(E(rc[o],0));
}
 
void Add(int o,int l,int r,int ql,int qr,int now)
{
    if (ql <= l && r <= qr)
    {
        v[now].push_back(E(o,1));
        ++du[o]; return;
    }
    int mid = l + r >> 1;
    if (ql <= mid) Add(lc[o],l,mid,ql,qr,now);
    if (qr > mid) Add(rc[o],mid+1,r,ql,qr,now);
}
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint(); s = getint(); Q.push(1);
    m = getint(); cnt = 1; Build(1,1,n);
    while (s--)
    {
        int p = getint(),d = getint();
        A[Num[p]] = d; bo[Num[p]] = 1;
    }
    while (m--)
    {
        int l = getint(),r,k,now = ++cnt;
        r = getint(); k = getint();
        for (int i = 1; i <= k; i++)
        {
            B[i] = getint(); ++du[now];
            v[Num[B[i]]].push_back(E(now,0));
        }
        if (l < B[1]) Add(1,1,n,l,B[1] - 1,now);
        if (B[k] < r) Add(1,1,n,B[k] + 1,r,now);
        for (int i = 1; i < k; i++)
            if (B[i] + 1 < B[i + 1]) Add(1,1,n,B[i] + 1,B[i + 1] - 1,now);
    }
    for (int i = 1; i <= cnt; i++) if (!bo[i]) A[i] = INF;
    while (!Q.empty())
    {
        int k = Q.front(); Q.pop();
        for (int i = 0; i < v[k].size(); i++)
        {
            E e = v[k][i]; --du[e.to];
            if (bo[e.to] && A[k] - e.w < A[e.to]) {puts("NIE"); return 0;}
            else if (!bo[e.to])
            {
                A[e.to] = min(A[e.to],A[k] - e.w);
                if (A[e.to] < 1) {puts("NIE"); return 0;}
            }
            if (!du[e.to]) Q.push(e.to);
        }
    }
    for (int i = 1; i <= cnt; i++) if (du[i]) {puts("NIE"); return 0;}
    puts("TAK"); for (int i = 1; i < n; i++) printf("%d ",A[Num[i]]); cout << A[Num[n]] << endl;
    return 0;
}

你可能感兴趣的:(dp,线段树)