BZOJ1107: [POI2007]驾驶考试egz

138 篇文章 0 订阅
77 篇文章 0 订阅

这题做了好久,性质很早就推出来了,一直不敢打…感觉总有什么地方不对劲…
事实上并没有…只是我线段树都写挂…

有n条向上的街道,每条街道上有些向左或向右的,通向相邻的街道的边,让你添加至多k条边,问最多有多少条街道能到达所有街道

首先,注意到我们从某条路出发,只能向上、左、右走,不能向下
然后每条边只通向相邻的街道,不会跨越

那么可以转化一下,把所有街道分成当前街道左边的,右边的,因为不能跨越,也不能向下,所以我们走到左边的街道再走到右边的街道这种操作是不存在的(因为不会比直接向右走更优
同时可以发现,只要向左能走到第1条街道=能到达左边的所有街道,右边同理
所以相当于问,添加K条边后,最多有多少条街道既能到1,又能到n

接着还有个很显然的性质:若i能到1,则i+1可能可以到1,但是若i不能到1,i+1就一定不能到1。 向右同理
所以若我们将添加进去的K条边分成向左的和向右的
那么对于向左的边,我们一定是先让2能到1,再让3能到1…..向右的同理
就是说,如果我们能算出分配i条边向左,最多能使1~f[i]都能到1,向右也这样算g[i],我们就能够通过枚举寻找最大值

接下来我们考虑怎么计算f[i]
设dp[i][j]表示第i条街道,在j的位置出发,最少要添加多少条边才能到1
直接转移肯定不行,考虑优化转移
从j出发,要么直接在j添加一条i->i-1的边,要么向上走到一条i->i-1的边
对于j往上走遇到的第一条街道,假设他在k的位置
dp[i][j]=min(dp[i-1][j]+1,dp[i-1][k])
我们发现,对于两条边之间的j,他们取min的对象是一样的,都是dp[i-1][k]
不妨用线段树做这件事,每个下标j维护当前的dp[i][j],对于两条边之间的部分,先区间+1,再对dp[i-1][k]取min
处理完当前的dp[i]后,判一下是否能用dp[i][0]更新f数组
于是我们就得到了f[i],g[i]同理

然后..就没有然后了,其实代码没多长
emmmmmmmmm我似乎讲的不够简洁
不管了

乐滋滋好强orz,把边反向转化成1,n到这些街道,然后最长不升子秒啊

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;

inline void down(int &x,const int &y){if(x>y)x=y;}
const int maxn = 210000;

int n,m,p,K;
int Li[maxn],Ri[maxn];
struct segment
{
    int flag,mn;
    segment(){mn=-1;}
}seg[maxn<<2];
void pushdown(const int x)
{
    int lc=x<<1,rc=lc|1;
    if(seg[x].flag)
    {
        int fl=seg[x].flag; seg[x].flag=0;
        if(seg[lc].mn!=-1) seg[lc].mn+=fl;
        seg[lc].flag+=fl;

        if(seg[rc].mn!=-1) seg[rc].mn+=fl;
        seg[rc].flag+=fl;
    }
    if(seg[x].mn!=-1)
    {
        int mn=seg[x].mn; seg[x].mn=-1;
        if(seg[lc].mn!=-1) down(seg[lc].mn,mn);
        else seg[lc].mn=mn;

        if(seg[rc].mn!=-1) down(seg[rc].mn,mn);
        else seg[rc].mn=mn;
    }
}
int lx,rx;
void add(const int x,const int l,const int r)
{
    if(rx<l||r<lx) return;
    if(lx<=l&&r<=rx) 
    {
        seg[x].flag++;
        if(seg[x].mn!=-1) seg[x].mn++;
        return;
    }
    pushdown(x);
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    add(lc,l,mid); add(rc,mid+1,r);
}
int Mn;
void upd(const int x,const int l,const int r)
{
    if(rx<l||r<lx) return;
    if(lx<=l&&r<=rx)
    {
        if(seg[x].mn==-1) seg[x].mn=Mn;
        else down(seg[x].mn,Mn);
        return;
    }
    pushdown(x);
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    upd(lc,l,mid); upd(rc,mid+1,r);
}
int loc;
int query(const int x,const int l,const int r)
{
    if(l==r) return min(seg[x].flag,seg[x].mn);
    pushdown(x);
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    if(loc<=mid) return query(lc,l,mid);
    else return query(rc,mid+1,r);
}
vector<int>V0[maxn],V1[maxn];

int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&K);
    for(int i=1;i<=p;i++)
    {
        int x,y,d; scanf("%d%d%d",&x,&y,&d);
        if(d) V0[x+1].push_back(y);
        else V1[x].push_back(y);
    }
    for(int i=1;i<=n;i++) 
        sort(V0[i].begin(),V0[i].end()),
        sort(V1[i].begin(),V1[i].end());

    int nowk=0; seg[1].mn=0;
    for(int i=2;i<=n;i++)
    {
        int las=-1;
        for(int j=0;j<V0[i].size();j++)
        {
            int nj=V0[i][j];
            if(las+1<nj)
            {
                lx=las+1,rx=nj-1; add(1,0,m);
                loc=nj; Mn=query(1,0,m);
                upd(1,0,m);
            }
            las=nj;
        }
        if(las+1<=m) lx=las+1,rx=m,add(1,0,m);

        loc=0; int nk=query(1,0,m);
        if(nk!=nowk)
            while(nowk<nk) Li[nowk++]=i-1;
    }
    while(nowk<K) Li[nowk++]=n;

    nowk=0; seg[1].flag=0; seg[1].mn=0;
    for(int i=n-1;i>=0;i--)
    {
        int las=-1;
        for(int j=0;j<V1[i].size();j++)
        {
            int nj=V1[i][j];
            if(las+1<nj)
            {
                lx=las+1,rx=nj-1; add(1,0,m);
                loc=nj; Mn=query(1,0,m);
                upd(1,0,m);
            }
            las=nj;
        }
        if(las+1<=m) lx=las+1,rx=m,add(1,0,m);

        loc=0; int nk=query(1,0,m);
        if(nk!=nowk)
            while(nowk<nk) Ri[nowk++]=i+1;
    }
    while(nowk<K) Ri[nowk++]=1;

    int ans=0;
    for(int i=0;i<=K;i++) if(Li[i]>=Ri[K-i])
        ans=max(ans,Li[i]-Ri[K-i]+1);
    int st=Li[0]>=Ri[0]?Li[0]-Ri[0]+1:0;
    printf("%d\n",ans-st);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值