bzoj4383

24 篇文章 0 订阅

题意:
给定一个长度为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)
Σk <= 300,000

#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<vector>
#define N 310000
#define inf 1e9
#define pb push_back
using namespace std;
struct node{int y,nex;}a[N];
struct node1{int l,r;};
struct node2{int l,r,lc,rc,p,mi,lz,lim;}lt[N];
int A[N],tl,n,m,du[N],fir[N],len,lim[N],ans[N];
vector<node1> v[N];
void ins(int x,int y)
{
    a[++len].y=y;a[len].nex=fir[x];fir[x]=len;
}
void bt(int l,int r)
{
    int now=++tl;
    lt[now].l=l;lt[now].r=r;lt[now].lim=inf;
    lt[now].p=l;
    if(l<r)
    {
        int mid=(l+r)/2;
        lt[now].lc=tl+1;bt(l,mid);
        lt[now].rc=tl+1;bt(mid+1,r);
    }
    else
    {
        if(A[l]!=-1) lt[now].lim=A[l];
        else lt[now].lim=inf;
    }
}
void down(int now)
{
    int lc=lt[now].lc,rc=lt[now].rc;
    if(lc) lt[lc].lz+=lt[now].lz;
    if(rc) lt[rc].lz+=lt[now].lz;
    lt[now].mi+=lt[now].lz;
    lt[now].lz=0;
}
void upd(int now)
{
    int lc=lt[now].lc,rc=lt[now].rc;
    if(lt[lc].lz) down(lc);
    if(lt[rc].lz) down(rc);
    if(lt[lc].mi<lt[rc].mi) lt[now].mi=lt[lc].mi,lt[now].p=lt[lc].p;
    else lt[now].mi=lt[rc].mi,lt[now].p=lt[rc].p;
}
void change(int now,int l,int r,int o,int d)
{
    int mid=(lt[now].l+lt[now].r)/2,lc=lt[now].lc,rc=lt[now].rc;
    if(lt[now].lz) down(now);
    if(lt[now].l==l && lt[now].r==r) 
        {lt[now].lz+=o;lt[now].lim=min(lt[now].lim,d);return;}
    if(mid>=r) change(lc,l,r,o,d);
    else if(l>mid) change(rc,l,r,o,d);
    else change(lc,l,mid,o,d),change(rc,mid+1,r,o,d);
    upd(now);
}
void init()
{
    int s;scanf("%d%d%d",&n,&s,&m);
    for(int i=1;i<=n;i++) A[i]=-1;
    for(int i=1;i<=s;i++)
    {
        int x;scanf("%d",&x);
        scanf("%d",&A[x]);
    }
    bt(1,n);
    for(int i=1;i<=m;i++) lim[i]=inf;
    for(int i=1;i<=m;i++)
    {
        int k,l,r,pre;scanf("%d%d%d",&l,&r,&k);
        pre=l;
        du[i]=k;
        while(k--)
        {
            int x;scanf("%d",&x);
            ins(x,i);
            if(pre<x) 
            {
                v[i].pb((node1){pre,x-1});
                change(1,pre,x-1,1,inf);
            }
            pre=x+1;
        }
        if(pre<=r) 
        {
            v[i].pb((node1){pre,r});
            change(1,pre,r,1,inf);
        }
    }
}
int find(int now,int k)
{
    int mid=(lt[now].l+lt[now].r)/2,lc=lt[now].lc,rc=lt[now].rc;
    if(lt[now].lz) down(now);
    if(lt[now].l==lt[now].r) return lt[now].lim;
    if(mid>=k) return min(find(lc,k),lt[now].lim);
    else return min(find(rc,k),lt[now].lim);
}
void make(int x)
{
    lim[x]--;
    int siz=v[x].size();
    for(int i=0;i<siz;i++) 
        change(1,v[x][i].l,v[x][i].r,-1,lim[x]);
}
void solve()
{
    int cnt=0;
    while(cnt<n)
    {
        if(lt[1].mi>0) {printf("NIE\n");return;}
        int x=lt[1].p;
        int d=find(1,x);
        if(d<=0) {printf("NIE\n");return;}
        change(1,x,x,inf,inf);
        if(A[x]!=-1 && d!=A[x]) {printf("NIE\n");return;}
        ans[x]=d;
        for(int k=fir[x];k;k=a[k].nex)
        {
            int y=a[k].y;
            lim[y]=min(lim[y],d);
            du[y]--;
            if(du[y]==0)
            {
                if(v[y].size()==0) continue;
                if(lim[y]<=1) {printf("NIE\n");return;}
                make(y);
            }
        }
        cnt++;
    }
    printf("TAK\n");
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}
int main()
{
    init();
    solve();
    return 0;
}

题解:
注意这题的本质是找一个合法拓扑序。
对于每个限制(k,l,r)我们给他新建一个点,让这k个人指向这个点,这个点再指向其余所有点,跑拓扑即可。暴力连边有O(nm)条边,注意新点向[l,r]之中O(k)个连续的区间连边,于是可以用线段树区间加减解决。线段树好慢啊
我的做法复杂了些,看题解都直接在线段树上建边,拿线段树的点跑拓扑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值