• [BZOJ4383][POI2015] Pustynia-[线段树+dp+拓扑排序]


    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个数大(严格大于,即没有等号)。
    请任意构造出一组满足条件的方案,或者判断无解。
    输入格式:
    第一行包含三个正整数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

    Solution

    本题的思路是:假如x比y大,则连一条边x->y。

    由于这里的是有k个数是比其他在某个范围内的数要大并且k还有个范围,建图的过程我们考虑用线段树优化。

    针对每一组x新建一个节点now,将所有的x[i]连过来,再由now出发,按照[l,r]被不同的x[1]-x[k[i]]分为的若干区间,去找线段树上的节点连边。

    接下来按照拓扑序dp。假如点x是被确定为c的,然而递推过来却发现dp[x]比c还小那就肯定无解了(我们在dp的时候使dp[x]会尽量大)。

    设当前队首节点为u,如果u不是线段树上的点也不是后来新建的节点now之一,则要求它的所有转移为dp[u]-1。反之转移为dp[u]即可。

    这道题我场上想出来这么搞了但出于某种认为它可能会MLE的心理默默遁了。。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cmath>
    using namespace std;
    int n,s,m;
    int num[600010],p,d;
    bool _is[600010],_sure[600010];
    
    struct node{int y,nxt;}g[6000010];int h[600010],tot=0;int In[600010];
    void add(int x,int y)
    { g[++tot]=node{y,h[x]};h[x]=tot;In[y]++;}
    struct Seg_tree
    {
    
        int lc[300010],rc[300010],rt,cnt;    
        void build(int &k,int l,int r)
        {
            k=++cnt;
            if (l==r) { add(k,l);return;}
            int mid=(l+r)/2;
            build(lc[k],l,mid);build(rc[k],mid+1,r);
            add(k,lc[k]);add(k,rc[k]);
        }
        void link(int k,int l,int r,int p,int askx,int asky)
        {
            if (askx<=l&&r<=asky) {add(p,k);return;}
            int mid=(l+r)/2;
            if (askx<=mid) link(lc[k],l,mid,p,askx,asky);
            if (asky>mid) link(rc[k],mid+1,r,p,askx,asky);
        }
    }Seg;
    queue<int>q;
    int x,y,js;
    void bfs()
    {
        for (int i=1;i<=Seg.cnt;i++) if (!In[i]) q.push(i);
        
        while (!q.empty())
        {
            x=q.front();q.pop();
            if (!_is[x]) num[x]=1000000000,_is[x]=1;
            js=x<=n?num[x]:num[x]+1;
            for (int i=h[x];i;i=g[i].nxt)
            {
                In[y=g[i].y]--;
                if (!In[y]) q.push(y);
                if (!_sure[y])
                {
                    if (!_is[y]) num[y]=js-1,_is[y]=1;
                    else num[y]=min(js-1,num[y]);
                } else if (_sure[y]&&num[y]>=js) {printf("NIE");exit(0);}
            }
        }
    }
    int l,r,k,_k[300100];
    int main()
    {
        scanf("%d%d%d",&n,&s,&m);
        Seg.cnt=n;
        Seg.build(Seg.rt,1,n);
        for (int i=1;i<=s;i++) {scanf("%d%d",&p,&d);num[p]=d;_is[p]=_sure[p]=1;}
    
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&l,&r,&k);
            int now=++Seg.cnt;        
            for (int j=1;j<=k;j++) 
            {
                scanf("%d",&_k[j]);add(_k[j],now);
            }
            if (_k[1]>l) Seg.link(Seg.rt,1,n,now,l,_k[1]-1);
            if (_k[k]<r) Seg.link(Seg.rt,1,n,now,_k[k]+1,r);
            for (int j=1;j<k;j++)
            if (_k[j+1]-_k[j]>1) Seg.link(Seg.rt,1,n,now,_k[j]+1,_k[j+1]-1);
        }
        bfs();
        for (int i=1;i<=n;i++) if (num[i]<1) {printf("NIE");return 0;}
        //for (int i=1;i<=n;i++) if (!_is[i]) {printf("NIE");return 0;}
        printf("TAK
    ");
        for (int i=1;i<=n;i++) printf("%d ",num[i]);
    }
  • 相关阅读:
    <记录> axios 模拟表单提交数据
    PHP 设计模式(一)
    CSS3中translate、transform和translation的区别和联系
    微信小程序 支付功能 服务器端(TP5.1)实现
    微信小程序 用户登录 服务器端(TP5.1)实现
    <记录> curl 封装函数
    <记录>TP5 关联模型使用(嵌套关联、动态排序以及隐藏字段)
    PHP/TP5 接口设计中异常处理
    TP5 自定义验证器
    高并发和大流量解决方案--数据库缓存
  • 原文地址:https://www.cnblogs.com/coco-night/p/9532024.html
Copyright © 2020-2023  润新知