UOJ Logo kczno1的博客

博客

共找到 6 篇包含 “难题” 标签的博客:

小C的字符串的简单做法

2018-07-03 23:42:30 By kczno1

https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1825

好久之前的坑.今天想了一下,忽然发现了一个简单做法.

记两个串为$a,b$,长度为$n,m$

设$dp[i][j]$表示$a_{1..i}$和$b_{1..j}$的$lcs$

考虑从$1$到$m$枚举$h$,动态维护无视$b_{1..h}$的$dp$数组(也就是$dp[i][j]$表示$a_{1..i}$和$b_{h+1..j}$的$lcs$)

假如现在枚举到$h$,对于$h$这一列,第一个变化的$dp[i][h]$必定满足$a[i]==b[h]$,并且$dp[i..n][h]$全部会减$1$.

再依次考虑列$h+1,..m$的变化,可以发现每一列变化的都是一个后缀,并且不长于前一列.

所以枚举每一列,维护后缀长度即可.

现在唯一的问题就是判断一个点可以从哪里转移过来.

每次变化的转移边只有轮廓上那些,所以可以轻松维护.

时间$O(n^2)$

(然而被卡常了...不是很懂为什么$n^2$要出成$5000$)

#include<bits/stdc++.h>
using namespace std;

template <typename T> void chmin(T&x,const T &y)
{
    if(x>y)x=y;
}
template <typename T> void chmax(T &x,const T &y)
{
    if(x<y)x=y;
}
typedef long long s64;
typedef unsigned long long u64;
typedef unsigned int u32;
typedef pair<int,int> pii;
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,r,l) for(int i=r;i>=l;--i)
#define rep0(i,l,r) for(int i=l;i<r;++i)
#define gc (c=getchar())
int read()
{
    char c;
    while(gc<'-');
    if(c=='-')
    {
        int x=gc-'0';
        while(gc>='0')x=x*10+c-'0';
        return -x;
    }
    int x=c-'0';
    while(gc>='0')x=x*10+c-'0';
    return x;
}
#undef gc

const int N=5000+5,D=998244353;
bool fr[N][N][3];
char s1[N],s2[N];
int dp[N][N],dpn[N];

int main()
{
#ifdef kcz
    freopen("1.in","r",stdin);//freopen("1.out","w",stdout);
#endif
    scanf("%s%s",s1+1,s2+1);
    int n=strlen(s1+1),m=strlen(s2+1);
    rep(i,1,n)
    rep(j,1,m)
    {
        int a=dp[i][j-1],b=dp[i-1][j-1]+(s1[i]==s2[j]),c=dp[i-1][j];
        dp[i][j]=max(max(a,b),c);
        fr[i][j][0]=a==dp[i][j];
        fr[i][j][1]=b==dp[i][j];
        fr[i][j][2]=c==dp[i][j];
    }
    rep(j,1,m)dpn[j]=dp[n][j];
    s64 ans=0;
    rep(h,1,m)
    {
        rep(j,h,m)ans=(ans*233+dpn[j])%D;
        int i=1;
        while(fr[i][h][0]||fr[i][h][2])++i;
        if(i>n)continue;
        rep(j,h+1,m)
        {
            if(!fr[i][j][1]&&!fr[i][j][2])fr[i][j][1]=fr[i][j][2]=1;
            else
            {
                fr[i][j][0]=0;
                while(++i,fr[i][j][2])fr[i][j][0]=fr[i][j][1]=0;
                if(i>n)break;
                fr[i][j][2]=1;
            }
            --dpn[j];
        }
    }
    cout<<ans;
}

[Ynoi2018]末日时在做什么?有没有空?可以来拯救吗?题解&&求证复杂度

2018-01-26 11:57:02 By kczno1

分块

块内需要维护前缀后缀全局的最大子段和

前缀后缀的求个凸包就行了

全局的话,每次求出当前最大子段和及长度,并求出使长度变化全局至少要加多少

因为加的是正数,所以一旦变化长度一定是变长的

所以只会变$n$次

求这个可以直接二分,但是是$\log{值域}$的

更好的方法是考虑以$i$为右端点时,就是要求$\min{(ans_{s}-(s_{i}-s_{j}))/((i-j)-ans_{len})}$,其中$j < i-ans_{len}$

所以可以从左到右枚举$i$,维护$(j,-s_{j})$的凸包,然后在凸包上二分.

时间$O(n \sqrt{n \log{n} })$

但是其实还有一个问题,就是当加在一个块的一个部分时,其答案长度可能变短

这样时间就没法证了..

我既不会证也不会卡,只能求出大佬了..

(其实我向$lxl$要了题解..但感觉太麻烦了而且常数大就写了这个)

(如果能有人优化下面代码或者自己写一个然后打败标算也是极好的)

(其实这个代码是很难过的,我在$luogu$上试了十几个分块长度才过的,不过一过就踩了标算.)


#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define pb push_back
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,r,l) for(int i=r;i>=l;--i)
#define mid (l+r>>1)

template <typename T> inline void chmin(T &x,const T &y)
{
    if(x>y)x=y;
}
template <typename T> inline void chmax(T &x,const T &y)
{
    if(x<y)x=y;
}

const int ch_top=6e6;
char ch[ch_top],*now_r=ch-1;
char ch_w[ch_top],*now_w=ch_w-1;
#define gc (*++now_r)
#define c (*now_r)
int read()
{
    while(gc<'-');
    if(c=='-')
    {
        int x=gc-'0';
        while(gc>='0')x=x*10+c-'0';
        return -x;
    }
    int x=c-'0';
    while(gc>='0')x=x*10+c-'0';
    return x;
}
int readz()
{
    while(gc<'-');
    int x=c-'0';
    while(gc>='0')x=x*10+c-'0';
    return x;
}
#undef c
void write(ll &ans)
{
    if(ans<10){*++now_w='0'+ans;*++now_w='\n';return ;}
    static char st[20];
    int top=0;
    do{st[++top]='0'+ans%10;}while(ans/=10);
    do{*++now_w=st[top];}while(--top);
    *++now_w='\n';
}

#define B(x) ((x-1)/L)
const int N=1e5+5,L=/*4*/192,K=N/L+5;
const ll inf=1e11+2e9+1;
int n;
ll a[N];
struct TU
{
int q[L];ll s[L];
int head,tail;
};
struct BLOCK
{
int l,r;
ll ans;int anslen;ll nex;
ll ad;
TU pre,suf;ll sum;
bool full;
};
BLOCK block[K],*b,*e,*belong[N];

namespace BUILD
{
ll a[L];
ll ns[L];int nq[L];
ll ans;int anslen;
bool ok(const ll &x)
{
    ll s=0,li=ans+anslen*x;
    rep(i,0,L-1)
    {
        s=(s+a[i]+x<0)?0:s+a[i]+x;
        if(s>li)return 1;
    }

    return 0;
}
void build(BLOCK &k,bool init=1)
{
    ll x=k.ad,*_a=::a+k.l;
    k.ad=0;
    rep(i,0,L-1)a[i]=(_a[i]+=x);
    ll s;
   ans=0;anslen=0;
    s=0;int len=0;
    rep(i,0,L-1)
    {
        s+=a[i];
        if(s<0){s=0;len=0;}
        else
        {
            ++len;
            if(s>ans){ans=s;anslen=len;}
            else
            if(s==ans&&len>anslen)anslen=len;
        }
    }
    k.ans=ans;k.anslen=anslen;

    if(/*!ok(inf)*/anslen==L){k.nex=inf;k.pre.head=k.suf.head=k.pre.tail=k.suf.tail=0;k.pre.s[0]=k.suf.s[0]=k.sum=ans;k.full=1;return ;}
if(init)
{
    int /*head=0,*/tail=-1;
    ll *ns=k.pre.s;int *nq=k.pre.q;
    s=0;
    rep(i,1,L)
    {
        s+=a[i-1];
        while(tail>=0&&ns[tail]<=s)--tail;
        while(tail>=1&&(nq[tail-1]-i)*(ns[tail]-s)>=(nq[tail]-i)*(ns[tail-1]-s))--tail;
        ++tail;
        nq[tail]=i;ns[tail]=s;
    }
    k.pre.head=/*head*/0;k.pre.tail=tail;
    k.sum=s;

    /*head=0;*/tail=-1;
    ns=k.suf.s;nq=k.suf.q;
    s=0;
    rep(i,1,L)
    {
        s+=a[L-i];
        while(tail>=0&&ns[tail]<=s)--tail;
        while(tail>=1&&(nq[tail-1]-i)*(ns[tail]-s)>=(nq[tail]-i)*(ns[tail-1]-s))--tail;
        ++tail;
        nq[tail]=i;ns[tail]=s;
    }
    k.suf.head=/*head*/0;k.suf.tail=tail;
}
else
{
    ll *ns=k.pre.s;int *nq=k.pre.q;
    int i=k.pre.tail,head=k.pre.head;
    ns[i]+=x*nq[i];
    k.sum=ns[i];
    while(--i>=head)
    {
        ns[i]+=x*nq[i];
        if(ns[i]<=ns[i+1]){k.pre.head=i+1;break;}
    }


    ns=k.suf.s;nq=k.suf.q;
    i=k.suf.tail;head=k.suf.head;
    ns[i]+=x*nq[i];
    while(--i>=head)
    {
        ns[i]+=x*nq[i];
        if(ns[i]<=ns[i+1]){k.suf.head=i+1;break;}
    }
}
/*  ll l=0,r=10000;
    while(!ok(r)){r<<=1;if(r>=inf){r=inf;break;}}
    while(l+1!=r)
    if(ok(mid))r=mid;
    else l=mid;
    k.nex=r;*/
    ll nex=inf;
static bool may[L];
    s=0;
    per(i,L-1,anslen)
    {
        may[i]=s<=0;
        s=s+a[i]<0?0:s+a[i];
    }

    s=0;ll y=ans;
    int top=0;
    ns[0]=0;nq[0]=-1;
    int i=anslen,j=0;
    rep(k,0,i-1)y-=a[k];
    while(i<L)
    {
        s-=a[j];y-=a[i];

        if(may[i])
        {   
        //int x=i-anslen;
#define x j
        if(top<5)
        {
            rep(h,0,top)
            if((x-nq[h])*nex>y-ns[h])nex=(y-ns[h])/(x-nq[h]);
        }
        else
        {
            int l=0,r=top;
            while(l!=r)
            if((nq[mid+1]-x)*(ns[mid]-y)>=(nq[mid]-x)*(ns[mid+1]-y))l=mid+1;
            else r=mid;

            if((x-nq[l])*nex>y-ns[l])nex=(y-ns[l])/(x-nq[l]);
        }
#undef x
        }

        while(top>=1&&(nq[top-1]-j)*(ns[top]-s)>=(nq[top]-j)*(ns[top-1]-s))--top;
        if(s>ns[top]){ns[++top]=s;nq[top]=j;}
        ++i;++j;
    }

    k.nex=ok(nex)?nex:nex+1;
//  printf("%lld\n",k.nex);
}
};
using BUILD::build;

int main()
{
//  freopen("1.in","r",stdin);freopen("1.out","w",stdout);
    ch[fread(ch,1,ch_top,stdin)]=0;
    n=readz();int m=readz();
    rep(i,1,n)a[i]=read();
    b=block;e=b-1;
    for(int l=1;l<=n;)
    {
        int r=l+L-1;
        if(l==1)r=l+L*2; 
        if(l+L*2>=n)r=n;
        ++e;
        e->l=l;e->r=r;
        while(l<=r){belong[l]=e;++l;}
    }
    for(BLOCK *p=b+1;p<e;++p)build(*p);
    while(m--)
    {
        while(gc<'0');
        if(*now_r=='1')
        {
            int l=readz(),r=readz(),x=readz();
            BLOCK *bl=belong[l],*br=belong[r];
            if(bl==br)
            {
                rep(i,l,r)a[i]+=x;
                if(bl>b&&bl<e)
                {
                    if(bl->full)bl->ans+=(ll)x*(r-l+1);
                    else build(*bl);
                }
            }
            else
            {
                int bl_r=bl->r;
                rep(i,l,bl_r)a[i]+=x;
                if(bl>b)
                {
                    if(bl->full)bl->ans+=(ll)x*(bl_r-l+1);
                    else build(*bl);
                }

                while(++bl<br)bl->ad+=x;

                int br_l=br->l;
                rep(i,br_l,r)a[i]+=x;
                if(br<e)
                {
                    if(br->full)br->ans+=(ll)x*(r-br_l+1);
                    else build(*br);
                }
            }
        }
        else
        {
            int l=readz(),r=readz();
        //  if(r-l<=L*2)
            if(belong[l]==belong[r])
            {   
                ll ans=0,s=0,x=belong[l]->ad;
                rep(i,l,r)
                {
                    s=(s+a[i]+x<0)?0:s+a[i]+x;
                    if(s>ans)ans=s;
                }
                write(ans);
                continue;
            }
            ll ans=0,s=0;
            BLOCK *bl=belong[l],*br=belong[r];
            int bl_r=bl->r;ll x=bl->ad;
            rep(i,l,bl_r)
            {
                s=(s+a[i]+x<0)?0:s+a[i]+x;
                if(s>ans)ans=s;
            }
            while(++bl<br)
            {
                x=bl->ad;
                if(bl->full)
                {
                    s+=bl->ans+x*L;
                    if(s>ans)ans=s;
                }
                else
                if(x>=bl->nex)
                {
                    build(*bl,0);
                    if(bl->ans>ans)ans=bl->ans;
                    if(s+bl->pre.s[bl->pre.head]>ans)ans=s+bl->pre.s[bl->pre.head];
                    s=max(0LL,max(s+bl->sum,bl->suf.s[bl->suf.head]));
                }
                else
                {
                    if(bl->ans+x*bl->anslen>ans)ans=bl->ans+x*bl->anslen;
                #define get q->s[q->head]+x*q->q[q->head]
                #define pop while(q->head<q->tail&&q->s[q->head]<q->s[q->head+1]+x*(q->q[q->head+1]-q->q[q->head]))++q->head;
                    TU *q=&bl->pre;
                    pop
                    if(s+get>ans)ans=s+get;
                    q=&bl->suf;
                    pop
                    s=max(0LL,max(s+bl->sum+x*L,get));
                #undef get
                #undef pop
                }
            }
            int br_l=br->l;x=br->ad;
            rep(i,br_l,r)
            {
                s=(s+a[i]+x<0)?0:s+a[i]+x;
                if(s>ans)ans=s;
            }
            write(ans);
        }
    }
    fwrite(ch_w,1,now_w-ch_w+1,stdout);
}

51nod 矩阵中不重复的元素V3 题解

2017-07-23 18:37:59 By kczno1

题目链接

先考虑 $x^a=y^b$ 意味着什么

记 $p(x)$ 为最小的 $p$, 使得 $\exists k , p^k=x$。 那么 $x^a=p(x)^{k(x) \times a}$

则 $ x^a=y^b $ 等价于 $ p(x)^{k(x) \times a}=p(y)^{k(y) \times b}$ 等价于 $ p(x) = p(y) \land k(x) \times a = k(y) \times b $

考虑枚举 $p$ ,计算所有 $p(x)=p$ 的 $x$ 带来的重复

那么 使 $x$ 在给定区间 的 $k(x)$ 在一个区间中

于是问题转化为,求 $ card \{ i \times j \, | \, i \in [l_1,r_1] \, , j \in [l_2,r_2] \} $其中$l_1$,$r_1$为$O(log_2(n))$级别,$l_2$,$r_2$为 $O(n)$ 级别

容斥。

先加上在 $[l_1,r_1]$ 中任选一个数,并在 $[l_2,r_2]$ 任选一个数的方案数

再减去在 $[l_1,r_1]$ 任选两个数 $x,y$,并在 $[l_2,r_2]$ 任选两个数 $a,b$ ,使得$x \times a=y \times b$的方案数

再加上在 $[l_1,r_1]$ 任选三个数 $x,y,z$,并在 $[l_2,r_2]$ 任选三个数 $a,b,c$ ,使得$x \times a=y \times b=z \times c$的方案数 ......

可以发现 当 $[l_1,r_1]$ 中的数确定之后, $[l_2,r_2]$ 的方案数是可以算出来的 因为乘积一定为 $lcm$ 的倍数

设为 $x$ 倍 则$l_2 \le x \times lcm/max \, \& \& \, x \times lcm/min \le r_2$ 于是 $l_2 \times max \le x \times lcm \le r_2 \times min$

所以 $x$ 的取值数=$r_2 \times min/lcm - l_2 \times max/lcm +1$

于是暴力容斥复杂度 $=O(2^{r_1-l_1+1})=O(n)$

$l_1$,$r_1$一共有 $ log_2^2(n) $种

对于所有的一样的$l_1$,我们可以从小到大枚举$r_1$计算。 总复杂度还是 $O(n)$

当然还过不了

注意到 如果一个数选了之后对于$lcm,max,min$没有影响 那么他选和不选的贡献就会抵消

所以先枚举$max,min$ 之后$dfs$ 如果碰到对$lcm$无贡献的数,就直接退出

同时,如果选了它导致前面的数成为对$lcm$无贡献的数,那么它就不选

这样优化之后复杂度就很好了 李陶冶说"比较接近于光滑数的数量(其实比这个还要低一个数量级)"

但我不知道光滑数是什么东西 总之O(跑的过)

还有个问题,就是如何求出有多少个$p(x)$会使得$k(x)$在 $[l_1,r_1]$ 中

我是筛出$ \sqrt{n} $以内所有$p(x)=x$的$x$ 然后枚举 求更优的方法?

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int D=1e9+7,Lo=52;
int cnt[Lo+2][Lo+2];
ll L1,R1,l2,r2,ans,now;
void init()
{
    ll m,n,a,b;
    cin>>m>>n>>a>>b;
    ans=(m%D)*(n%D)%D;
    L1=a;R1=a+n-1;l2=b;r2=b+m-1;
}

int Gcd[Lo+2][Lo+2];
int gcd(int x,int y)
{
    while(y)swap(x%=y,y);
    return x;
}

int l1,r1,mn;
ll L,R;//L(=l2*mx)<=x*Lcm<=R(=r2*mn)   
int mx_p[Lo+5],st[Lo+5],*top;
#define len (R/Lcm - (L+Lcm-1)/Lcm +1)
void dfs(int *h,const ll &Lcm,bool ji)
{
    if(Lcm>R)return ;
    if(h>top)
    {
        if(ji)now-=len;
        else now+=len;
        return ;
    }
    int x=*h;
    if(Lcm%x==0) return ;
    //if(len<=0) return ;
    dfs(h+1,Lcm,ji);
    dfs(h+1,Lcm*(x/Gcd[x][Lcm%x]),ji^1);
}

const int U=8e7;
bool mark[U];

int main()
{
    //freopen("1.in","r",stdin);freopen("1.out","w",stdout);
    init();
    int mp=sqrt(R1);
    for(int p=2;p<=mp;++p)
    if(!mark[p])
    {
        int k=0;
        ll x=1;
        while(x<L1&&x<=R1/p) { x*=p;if(x<=mp)mark[x]=1;++k; }
        if(x<L1||x>R1)continue;
        int l1=k; 
        while(x<=R1/p) { x*=p;if(x<=mp)mark[x]=1;++k; }
        if(k>l1)
        ++cnt[l1][k];
    }

    for(int i=1;i<=Lo;++i)
    for(int j=0;j<i;++j)
     Gcd[i][j]=gcd(i,j);
    for(int i=1;i<=Lo;++i)
    for(int j=1;j<i;++j)
    if(i%j==0) mx_p[i]=j; 

    for(l1=1;l1<=Lo;++l1)
    {
        int mr=0;
        for(r1=Lo;r1>l1;--r1)
        if(cnt[l1][r1]){mr=r1;break;}

        now=0;
        for(r1=l1+1;r1<=mr;++r1)
        {        
            L=l2*r1;
            for(mn=l1;mn<r1;++mn)
            {
               R=r2*mn;    
               if(L>R)continue;

               int Lcm=r1*(mn/gcd(r1,mn));
               int i=mn+1;
               for(;i<r1;++i)
               if(Lcm%i==0) break;
               if(i<r1)continue;

               top=st-1;
               for(int i=mn+1;i<r1;++i)
               if(mx_p[i]<=mn) 
                *++top=i;

               dfs(st,Lcm,0); 
            }
            now%=D;
            ans-=cnt[l1][r1]*now%D;
        }
    }
    cout<<(ans%D+D)%D;
}

哪里有这题题解啊

2017-07-18 20:55:29 By kczno1

bzoj 舌尖上的由乃

2017-05-14 10:22:59 By kczno1

我只想到了n根号(nlog)log的分块,感谢出题人教了我如何n根号log。

(假设分k块)

每个块内都排好序。

加的时候
整块的就打标记 O(k)
零散的,
考虑打标记后,没打标记的和打了标记的部分顺序都还是不变,
遍历一遍原来排好的顺序,就能得到这两个部分,之后归并一下就可以了。O(n/k)

求k大
先二分 转为check一个区间有多少数比一个数小。

按普通的想法
整块的二分 O(klog)
零散的暴力 O(n/k)
所以一次复杂度是O(klog^2+nlog/k)
k取根号(n/log),总复杂度就是O(n根号(nlog)log)

注意到,check零散的时间是O(nlog/k),这还可以优化。
首先,用遍历一遍原来顺序的方法 O(n/k)把两个零散的各自排序。
之后check的时候就可以二分了,O(log^2)
所以一次复杂度变成O(klog^2+n/k)
k取根号n/log,总复杂度就是O(n根号nlog)

还有一个方法,是跟值域有关的。

因为值域小了,就可以处理出每个块的前缀和

分块使得
1 每个块max-min<=x1*len
2 块的大小也不超过x2
3 块尽量大

如果不考虑2的话 块是n/x1个的
因为所有相邻两数的差的和是nlen的 因为n次区间加,每次加len
而相邻两块的max-min>x1
len
说明相邻两块所有相邻两数的差>max-min>x1len
所以块数<n
len/x1*len=n/x1

现在再把2考虑进来 相当于说一些大于x2的块要被拆掉
设大于x2的块的大小-x2的和是sum 那么每拆一次 sum-=x2
所以最多拆n/x2次

所以块数是n/x1+n/x2的。
因为所有块max-min的和<nlen,所以build的时间是nlen

check的时候
整块就可以O(1)
零散的暴力 就是x2
时间O(log*(n/x1+n/x2+x2))

加的时候整块打标记,O(n/x1+n/x2)
零散的暴力重算块的前缀和,O(x2+x1*len)

注意根号次加法后,块的max-min可能又大了根号*len,

这个时候要再重构一次分块。

时间(根号nlen)

x1,x2应该都要取根号

总时间O(n根号log+n根号len)

因为感觉第一种比较好写 我只写了第一种

#include<bits/stdc++.h>
using namespace std;

#define mid (l+r>>1)

const int ch_top=10000000;
char ch[ch_top],*now_r=ch,*now_w=ch-1;
int read()
{
    while(*now_r<'0')++now_r;
    int x=*now_r-'0';
    while(*++now_r>='0') x=x*10+*now_r-'0';
    return x; 
}
void write(int x)
{
    static char st[20];static short top;
    if(!x)*++now_w='0';
    else
    {
        for(;x;x/=10)st[++top]='0'+x%10;
        for(;top;--top)*++now_w=st[top];
    }
    *++now_w='\n';
}

const int N=100005,L=1500,K=N/L+5;
int f[N],w[N];vector<int>son[N];
typedef vector<int>::iterator ii;
int a[N],b[N];//b[i]是块排好序后的人 

int q[N],t,qa[N],la,qb[N],lb;
void merge_to(int *q)
{
    if(!lb) { memcpy(q+1,qa+1,la*sizeof(int));return ; }
    if(!la) { memcpy(q+1,qb+1,lb*sizeof(int));return ; }
    t=0;
    int ha=1,hb=1;
    for(;;)
    if(a[qa[ha]]<a[qb[hb]])
    {
        q[++t]=qa[ha];
        if(++ha>la) 
        {
            do
            {
                q[++t]=qb[hb];
            }while(++hb<=lb);
            break;
        }
    }
    else
    {
        q[++t]=qb[hb];
        if(++hb>lb) 
        {
            do
            {
                q[++t]=qa[ha];
            }while(++ha<=la);
            break;
        }
    }
}

int kl[K],kr[K],ad[K];
bool a_xiao(int x,int y)
{
    return a[x]<a[y];
}

void chmin(int &x,int y)
{
    if(x>y)x=y;
}
void chmax(int &x,int y)
{
    if(x<y)x=y;
}

int dfn[N],out[N],tot;
void dfs(int x,int fr,int now)
{
    now+=w[x];
    a[dfn[x]=++tot]=now;
    for(ii it=son[x].begin();it!=son[x].end();++it)
     dfs(*it,x,now);
    out[x]=tot;
}

int nl,nr;
int erfen(int *q,int t,int now)
{
    if(a[q[1]]>now)return 0;
    if(a[q[t]]<=now)return t;
    int l=1,r=t;
    while(l+1!=r)
    if(a[q[mid]]<=now) l=mid;
    else r=mid;
    return l;
}
int xiao_deng(int now)
{
   int ans=erfen(qa,la,now)+erfen(qb,lb,now);
   for(int k=nl+1;k<nr;++k) ans+=erfen(b+kl[k]-1,L/*kr[k]-kl[k]+1*/,now-ad[k]);
   return ans;
} 

void down(int k)
{
    int x=ad[k];
    if(!x)return ;
    for(int i=kl[k];i<=kr[k];++i) a[i]+=x;
    ad[k]=0;
}

int main()
{
    //freopen("1.in","r",stdin);freopen("3.out","w",stdout);
    ch[fread(ch,1,ch_top,stdin)]=0;
    int n=read(),m=read(),i,k;
    read();
    for(i=2;i<=n;++i)
    {
        son[f[i]=read()].push_back(i);
        w[i]=read();
    }

    dfs(1,0,0);

    tot=(n-1)/L;
    for(i=0;i<=tot+1;++i) {kl[i]=i*L+1;kr[i]=(i+1)*L;}
    kr[tot]=n;

    for(i=1;i<=n;++i)b[i]=i;
    for(k=0;k<=tot;++k)
        sort(b+kl[k],b+kr[k]+1,a_xiao);

    while(m--)
    {
        int type=read(),x=read();
        int ql=dfn[x],qr=out[x]; 
        //int ql=read(),qr=read();
        x=read();
        nl=(ql-1)/L;nr=(qr-1)/L;
        if(type==1)
        {
            int len=qr-ql+1;
            if(len<x) { *++now_w='-';*++now_w='1';*++now_w='\n';continue; }  
            down(nl);down(nr);
            if(len<=L*2)
            {
                down(nl+1);
                memcpy(q,a+ql,len*sizeof(int));
                nth_element(q,q+x-1,q+len);
                write(q[x-1]);
                continue;
            }
            else
            {
                la=0;
                for(i=kl[nl];i<=kr[nl];++i)
                if(b[i]>=ql) qa[++la]=b[i];
                lb=0;
                for(i=kl[nr];i<=kr[nr];++i)
                if(b[i]<=qr) qb[++lb]=b[i];
                //merge_to(q);

                int l=min(a[qa[1]],a[qb[1]]),r=max(a[qa[la]],a[qb[lb]]);
                for(k=nl+1;k<nr;++k) 
                {
                  chmin(l,a[b[kl[k]]]+ad[k]);
                  chmax(r,a[b[kr[k]]]+ad[k]); 
                }
                if(xiao_deng(l)>=x) { write(l);continue; }
                while(l+1!=r)
                {
                    if(xiao_deng(mid)>=x) r=mid;
                    else l=mid;
                }
                write(r);
            }
        }
        else
        {
            if(nl==nr)
            {
                for(i=ql;i<=qr;++i) a[i]+=x;
                la=lb=0;
                for(i=kl[nl];i<=kr[nl];++i) 
                if(b[i]>=ql&&b[i]<=qr) qa[++la]=b[i];
                else qb[++lb]=b[i];
                merge_to(b+kl[nl]-1);
                continue;
            }
            for(k=nl+1;k<nr;++k) ad[k]+=x;

            for(i=ql;i<=kr[nl];++i) a[i]+=x;
            la=0;lb=0;
            for(i=kl[nl];i<=kr[nl];++i)
            if(b[i]>=ql) qa[++la]=b[i];
            else qb[++lb]=b[i];
            merge_to(b+kl[nl]-1);

            for(i=kl[nr];i<=qr;++i) a[i]+=x;
            la=0;lb=0;
            for(i=kl[nr];i<=kr[nr];++i)
            if(b[i]<=qr) qa[++la]=b[i];
            else qb[++lb]=b[i];
            merge_to(b+kl[nr]-1);
        }
    }
    fwrite(ch,1,now_w-ch+1,stdout);
}

CF 480 div_1 E Parking Lot

2017-01-19 19:34:16 By kczno1

题意:n*m的矩阵,有一些点不能选。k次操作,每次都让一个点变成不可选,每次都问当前可选的最大正方形。n,m,k<=2000

我想到了一个方法。过掉了。

新加的点造成的新的正方形一定过那个点,也就一定过它所在的列。

用第j列更新答案时,从当前答案+1开始从小到大试。 由于答案最多增加n次,所以总的尝试次数是O(N+K)的。 每次检验,我们从上到下枚举区间,用单调队列维护当前两边的最小值,时间是O(N)的。 所以总时间O(N^2+NK)

(看了cf的官方题解,没看懂,但时间是nklogk的)

但如果是求矩形最大面积怎么做呢?!!(或许不可做?)

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

void chmax(int &x,int y)
{
    if(x<y)x=y;
}
#define N 2010
int l[N][N],r[N][N];//水平上延伸的极限点
bool ok[N][N];//可用的点 
int now;
bool open;
int n,m,k,len,i,j;
void dp_lr(int i)//dp第i行的l,r 
{
    for (j=1;j<=m;++j)
    if (ok[i][j])
     l[i][j]=l[i][j-1];
    for (j=m;j;--j)
    if (ok[i][j])
     r[i][j]=r[i][j+1]; 
}

struct queue
{
    int q[N],h[N],head,tail;
    void clear()
    {
       head=1;tail=0;
    }
    void push(int x)
    {
        while (tail>=head&&h[tail]>=x) --tail;
        ++tail;
        q[tail]=i;h[tail]=x;
    }
    int len()
    {
        while (i-q[head]>now) ++head;
        if (h[head]<0) return -N;
        return h[head];
    }
}ql,qr;
void dp_ud(int j)//第j列更新当前答案 
{
    for (;;)
    {
        ql.clear();qr.clear();
        for (i=1;i<=now;++i) 
        {
            ql.push(j-l[i][j]);
            qr.push(r[i][j]-j);
        }
        for (;i<=n;++i)
        {
            ql.push(j-l[i][j]);
            qr.push(r[i][j]-j);
            if (ql.len()+qr.len()>=now) break;
        }
        if (i>n) return ;
        ++now;
    }
}

char ch[N]; 

int o;
struct query
{
    int x,y,ans;
    void init()
    {
        scanf("%d%d",&x,&y);
        ok[x][y]=0;l[x][y]=y+1;r[x][y]=y-1;
    }
    void add()
    {
        ans=now;
        ok[x][y]=1;
        dp_lr(x);
        dp_ud(y);
    }
}p[N];

int main()
{ freopen("1.in","r",stdin);freopen("1.out","w",stdout); 
   while (scanf("%d%d%d",&n,&m,&k)!=EOF)
   {
    int i,j;now=0;
    for (i=1;i<=n;++i)
    {
        scanf("%s",ch+1);
        for (j=1;j<=m;++j) 
        if(ch[j]=='.') ok[i][j]=1;
        else { ok[i][j]=0;l[i][j]=j+1;r[i][j]=j-1; }
    }
    for (i=1;i<=k;++i) p[i].init();

    for (i=1;i<=n;++i) {l[i][0]=1;r[i][m+1]=m;dp_lr(i);}
    for (j=1;j<=m;++j) dp_ud(j);

    for (i=k;i;--i) 
     p[i].add();
    for (i=1;i<=k;++i) printf("%d\n",p[i].ans);
   }
}