@bzoj - [email protected] [POI2015] Pustynia

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定一个长度为 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]<r[i]<=n,1<=k[i]<=r[i]-l[i]),接下来 k[i] 个正整数 x[1], x[2], ..., x[k[i]] (l[i]<=x[1]<x[2]<...<x[k[i]]<=r[i]),表示这 k[i] 个数中的任意一个都比任意一个剩下的 r[i]-l[i]+1-k[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

@[email protected]

首先不考虑任何时间会炸空间会炸等种种问题,这是一道差分约束题。

然后我们来优化一下。

其一是建图的时候,边的数量过多这一问题,我们使用线段树来优化建图。

其二则是数据太大跑最短路跑不过的问题。我们根据图的特殊性来优化。

对于我们建出的图,会产生两类环:

第一类是源点连入某个点再由这个点连回来的情况,含义是一个数的上下界。

这种情况,我们可以仅保留它的下界(或者是上界,看差分约束的具体实现方法),事后再来判断它的上界是否合法。

第二类是不经过源点的环,这个环必然无解(因为它表示 a > b > c > ... > a,而这显然是不可能的)。

综上,我们可以将这个图改成无环的图。

然后就可以拓扑排序了。

@accepted [email protected]

为了防止它不会溢出,我判断上界 10^9 是在拓扑排序里面判断的。

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
const int MAXM = 200000;
const int MAXK = 300000;
const int MAXV = 4*MAXN + MAXK;
const int INF = int(1E9);
struct edge{
    int to, dis;
    edge *nxt;
}edges[20*MAXK + 10*MAXN + 5], *adj[MAXV + 5], *ecnt=&edges[0];
void addedge(int u, int v, int w) {
    edge *p = (++ecnt);
    p->to = v, p->dis = w, p->nxt = adj[u], adj[u] = p;
    //printf("(%d %d %d)\n", u, v, w);
}
int id[MAXN + 5], num[4*MAXN + 5], cnt;
void build_segtree(int x, int l, int r) {
    num[x] = (++cnt);
    if( l == r ) {
        id[l] = num[x];
        return ;
    }
    int mid = (l + r) >> 1;
    build_segtree(x<<1, l, mid);
    addedge(num[x<<1], num[x], 0);
    build_segtree(x<<1|1, mid + 1, r);
    addedge(num[x<<1|1], num[x], 0);
}
void build_edge_segment(int x, int l, int r, int pl, int pr, int p) {
    if( pl <= l && r <= pr ) {
        addedge(num[x], p, 0);
        return ;
    }
    if( pl > r || pr < l )
        return ;
    int mid = (l + r) >> 1;
    build_edge_segment(x<<1, l, mid, pl, pr, p);
    build_edge_segment(x<<1|1, mid + 1, r, pl, pr, p);
}
int x[MAXN + 5], key[MAXN + 5], n;
int ind[MAXV + 5], dis[MAXV + 5], stk[MAXV + 5], tp;
void solve() {
    for(int i=0;i<=cnt;i++) {
        for(edge *p=adj[i];p;p=p->nxt)
            ind[p->to]++;
        dis[i] = INF;
    }
    dis[0] = 0; stk[++tp] = 0;
    while( tp ) {
        int t = stk[tp--];
        for(edge *p=adj[t];p;p=p->nxt) {
            dis[p->to] = min(dis[p->to], dis[t] + p->dis);
            if( dis[p->to] < -INF ) {
                puts("NIE");
                return ;
            }
            ind[p->to]--;
            if( ind[p->to] == 0 ) stk[++tp] = p->to;
        }
    }
    for(int i=0;i<=cnt;i++)
        if( ind[i] ) {
            puts("NIE");
            return ;
        }
    for(int i=1;i<=n;i++)
        if( key[i] != -1 && key[i] + dis[id[i]] ) {
            puts("NIE");
            return ;
        }
    puts("TAK");
    for(int i=1;i<=n;i++)
        printf("%d%c", -dis[id[i]], (i == n) ? ‘\n‘ : ‘ ‘);
}
int main() {
    int s, m;
    scanf("%d%d%d", &n, &s, &m);
    build_segtree(1, 1, n);
    for(int i=1;i<=n;i++) {
        addedge(0, id[i], -1);
        key[i] = -1;
    }
    for(int i=1;i<=s;i++) {
        int p, d; scanf("%d%d", &p, &d);
        addedge(0, id[p], -d);
        key[p] = d;
    }
    for(int i=1;i<=m;i++) {
        int l, r, k; cnt++;
        scanf("%d%d%d", &l, &r, &k);
        x[0] = l - 1, x[k + 1] = r + 1;
        for(int j=1;j<=k;j++) scanf("%d", &x[j]);
        for(int j=1;j<=k+1;j++) build_edge_segment(1, 1, n, x[j-1] + 1, x[j] - 1, cnt);
        for(int j=1;j<=k;j++) addedge(cnt, id[x[j]], -1);
    }
    solve();
}

@[email protected]

什么?题目中还要求了它不能超过 10^9?

我一开始并没有看到 QAQ。调了好久啊 QAQ。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10359111.html

时间: 2024-08-28 04:19:34

@bzoj - [email protected] [POI2015] Pustynia的相关文章

@bzoj - [email&#160;protected] [POI2015] Kinoman

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 共有 m 部电影,第 i 部电影的好看值为 w[i]. 在 n 天之中每天会放映一部电影,第 i 天放映的是第 f[i] 部. 你可以选择 l, r (1 <= l <= r <= n) ,并观看第 l, l+1, -, r 天内所有的电影. 最大化观看且仅观看过一次的电影的好

@bzoj - [email&#160;protected] [POI2015] Wilcze do?y

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0. 请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p . input 第一行包含三个整数 n, p, d (1 <= d <= n <= 2000000,0 &

@bzoj - [email&#160;protected] [POI2015] Logistyka

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作: 1.U k a 将序列中第 k 个数修改为 a. 2.Z c s 在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作. 每次询问独立,即每次询问不会对序列进行修改. input 第一行包含两个

@bzoj - [email&#160;protected] [POI2015] Myjnie

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 家洗车店从左往右排成一排,每家店都有一个正整数价格 p[i]. 有 m 个人要来消费,第 i 个人会驶过第 a[i] 个开始一直到第 b[i] 个洗车店,且会选择这些店中最便宜的一个进行一次消费.但是如果这个最便宜的价格大于 c[i],那么这个人就不洗车了. 请给每家店指定一个

@bzoj - [email&#160;protected] [POI2015] Odwiedziny

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,树上每条边的长度都为 1 ,第 i 个点的权值为 a[i]. Byteasar 会按照某个 1 到 n 的全排列 b 走 n-1 次,第 i 次他会从 b[i] 点走到 b[i+1] 点,并且这一次的步伐大小为 c[i]. 对于一次行走,假设起点为 x,终点为

@bzoj - [email&#160;protected] [POI2015] ?asuchy

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 圆桌上摆放着 n 份食物,围成一圈,第 i 份食物所含热量为 c[i]. 相邻两份食物之间坐着一个人,共有 n 个人.每个人有两种选择,吃自己左边或者右边的食物.如果两个人选择了同一份食物,这两个人会平分这份食物,每人获得一半的热量. 假如某个人改变自己的选择后(其他 n-1 个人的选

@bzoj - [email&#160;protected] [POI2015] Wycieczki

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一张 n 个点 m 条边的带权有向图,每条边的边权只可能是1,2,3中的一种. 将所有可能的路径按路径长度排序,请输出第 k 小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点. input 第一行包含三个整数 n, m, k (1<=n<=40,1<=m<

@bzoj - [email&#160;protected] [Poi2011]Lightning Conductor

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @version - [email protected] @version - [email protected] @[email protected] @[email protected] 已知一个长度为

@bzoj - [email&#160;protected] [POI2014]Hotel加强版

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵树,求无序三元组 (a, b, c) 的个数,使得 dis(a, b) = dis(b, c) = dis(c, a),且 a ≠ b, b ≠ c, c ≠ a. inpu