BZOJ 3711 [PA2014]Druzyny

DP+分治+线段树

似乎有另一种 O(nlogn) 的做法,就不详细说了。说一种 O(nlog2n) 的做法。

考虑分治,对于区间 [l,r] ,用 [l,mid] 来贡献 [mid+1,r]
对于一组贡献,设 j[l,mid],i[mid+1,r]
j 能贡献给i ,当且仅当,
max{l[k]|j<kmid}ijmin{r[k]|j<kmid}
max{l[k]|mid+1ki}ijmin{r[k]|mid+1ki}
移项一下,
max{l[k]|j<kmid}+jimin{r[k]|j<kmid}+j
imin{r[k]|mid+1ki}jimax{l[k]|mid+1ki}
不等号左右两边分别只和 i,j 有关,预处理一下。
对于第一个限制可以把 j 分成两个事件点max{l[k]|j<kmid}+j,min{r[k]|j<kmid}+j,一个加入,一个删除。对于第二个限制用线段树即可。
时间复杂度 O(nlog2n) ,需要一定的常(底)数(层)优化技巧才能通过此题。反正我这样是没过。

#include<cstdio>
#include<algorithm>
#define N 1000005
#define cmax(u,v) (u)<(v)?(u)=(v):0
#define cmin(u,v) (u)>(v)?(u)=(v):0
#define ainline __inline__ __attribute__((always_inline)) 
#define MOD 1000000007
#define reg register
using namespace std;
namespace runzhe2000
{
    namespace io
    {
        const int MaxBuff = 1 << 20;
        char B[MaxBuff], *S = B, *T = B;
        #define getc() ((S == T) && (T = (S = B) + fread(B, 1, MaxBuff, stdin), S == T) ? 0 : *S++)
    }
    ainline int read()
    {
        using namespace io;
        #define RG reg
        RG char ch; RG int ans = 0; RG bool neg = 0;
        while(ch = getc(), (ch < '0' || ch > '9') && ch != '-')     ;
        ch == '-' ? neg = 1 : ans = ch - '0';
        while(ch = getc(), '0' <= ch && ch <= '9') ans = ans * 10 + ch - '0';
        return neg ? -ans : ans;
    }
    const int INF = 1<<29;
    struct event
    {
        int t, j, type;
        bool operator < (const event& that) const
        {
            if(t == that.t) return type > that.type;
            return t < that.t;
        }
    }ev[1500005];
    int n, l[N], r[N], lm[N], rm[N];
    struct Pair
    {
        int f, g;
        ainline void fuck(Pair& l, Pair& r)
        {
            *this=(l.f!=r.f?l.f>r.f?l:r:(Pair){l.f,(l.g+r.g)%MOD});  
        }
        ainline void operator += (const Pair& that)
        {
            if(f > that.f + 1) return;
            if(f <= that.f) *this = (Pair){that.f+1, that.g};
            else (g += that.g) %= MOD;
        }
    }P[N], null;
    struct seg
    {
        Pair p;
        int c;
    }t[1<<20];
    ainline int dmin(reg int a, reg int b){return a<b?a:b;}
    ainline int dmax(reg int a, reg int b){return a>b?a:b;}
    int ql, qr, qp;
    Pair *qpair;
    void build(reg int x, reg int l, reg int r)
    {
        t[x].p = null;
        t[x].c = 0;
        if(l == r) return;
        reg int mid = (l+r)>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
    }
    void query(reg int x, reg int l, reg int r)
    {
        if(!t[x].c) return;
        if(ql <= l && r <= qr) return (void) (*qpair += t[x].p);
        reg int mid = (l+r)>>1; 
        if(ql <= mid) query(x<<1,l,mid);
        if(mid < qr ) query(x<<1|1,mid+1,r);
    }
    void modi(reg int x, reg int l, reg int r)
    {
        t[x].c += ((qpair->f < 0) ? -1 : 1);
        if(l == r){t[x].p = *qpair; return;}
        reg int mid = (l+r)>>1;
        (qp <= mid)
            ? modi(x<<1,l,mid)
            : modi(x<<1|1,mid+1,r);
        t[x].p.fuck(t[x<<1].p,t[x<<1|1].p);
    }
    void _solve(reg int L, reg int R, reg int mid)
    {
        lm[mid] = l[mid+1]; rm[mid] = r[mid+1];
        for(int i = mid-1; i >= L; i--) lm[i] = dmax(lm[i+1], l[i+1]), rm[i] = dmin(rm[i+1], r[i+1]);
        for(int i = mid+1; i <= R; i++) lm[i] = dmax(lm[i-1], l[i]), rm[i] = dmin(rm[i-1], r[i]);
        reg int evcnt = 0;
        for(int j = L; j <= mid; j++)
        {
            if(lm[j] > rm[j] || P[j].f < 0) continue;
            ev[++evcnt] = (event){j+lm[j], j,  1};
            ev[++evcnt] = (event){j+rm[j], j, -1};
        }
        sort(ev+1, ev+1+evcnt);
        reg int oldcnt=evcnt;
        for(int i = mid+1; i <= R; i++) ev[++evcnt] = (event){i,i,0};
        std::inplace_merge(ev+1,ev+1+oldcnt,ev+1+evcnt);
        build(1,L,mid);
        for(int i = 1; i <= evcnt; i++)
        {
            int j = ev[i].j; 
            if(ev[i].type == 1) // add 
                qp=j,qpair=P+j,modi(1,L,mid);
            else if(ev[i].type == -1)  // del
                qp=j,qpair=&null,modi(1,L,mid);
            else
                if((ql=dmax(j-rm[j], L)) <= (qr=dmin(j-lm[j], mid)))
                    qpair=P+j,query(1,L,mid);
        }
    }
    void solve(int L, int R)
    {
        if(L == R) return;
        reg int mid = (L+R)>>1;
        solve(L,mid);
        _solve(L,R,mid);
        solve(mid+1,R);
    }
    void main()
    {
        n = read();
        null = (Pair){-INF,0};
        for(int i = 1; i <= n; i++)
        {
            l[i] = read(), r[i] = read();
            P[i] = null;
        }
        P[0] = (Pair){0,1};
        solve(0,n);
        if(P[n].f > 0) printf("%d %d\n",P[n].f,P[n].g);
        else printf("NIE\n");
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值