• 一揽子计划


    咕了!

    liaoliao的BZOJ题表,一题题做(虽然有些题做过)。。

    但是靖爷都说这些题没有意思,没有营养%%%

                      完成量(21/139)

    BZOJ1226: [SDOI2009]学校食堂Dining

      因为每个人的B值不超过7,所以上一个吃饭的人应该在这个人前面不超过7,后面也不超过7的位置上

      状压DP,设f[i][j][k]为前i-1个人都已经吃完饭了,第i到i+7个人的状态为j,最后一个吃饭的人为第i+k个人

      然后就可以转移

      对于j&1==1,就是说第i个人已经吃了饭了,那么f[i+1][j>>1][k-1]=f[i][j][k],两者的意义是一样的

      对于j&1==0,那么就枚举应当作为最后一个吃饭的人的位置i+p,f[i][j^(1<<p)][p]=f[i][j][k]+cal(i+k,i+p))

      然后因为k有可能为负,所以整体的k+8存入数组

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int T[1100],B[1100];
    int f[1100][310][21];
    int cal(int x,int y){return (x==0||y==0)?0:T[x]^T[y];}
    int main()
    {
        int cas;
        scanf("%d",&cas);
        while(cas--)
        {
            int n;
            scanf("%d",&n);
            for(int i=1;i<=n;i++) scanf("%d%d",&T[i],&B[i]);
            memset(f,63,sizeof(f));
            f[1][0][7]=0;
            for(int i=1;i<=n;i++)
            {
                for(int j=0;j<(1<<8);j++)
                {
                    for(int k=0;k<=15;k++)
                    {
                        if(f[i][j][k]==f[0][0][0]) continue;
                        if(j&1) f[i+1][j>>1][k-1]=min(f[i+1][j>>1][k-1],f[i][j][k]);
                        else
                        {
                            int mmax=1<<30;
                            for(int p=0;p<=7;p++)
                            {
                                if((j&(1<<p))==0)
                                {
                                    if(i+p>mmax) break;
                                    mmax=min(mmax,i+p+B[i+p]);
                                    f[i][j^(1<<p)][p+8]=min(f[i][j^(1<<p)][p+8],f[i][j][k]+cal(i+k-8,i+p));
                                }
                            }
                        }
                    }
                }
            }
            int ans=1<<30;
            for(int k=0;k<=8;k++) ans=min(f[n+1][0][k],ans);
            printf("%d
    ",ans);
        }
        return 0;
    }
    BZOJ1226

    BZOJ1260: [CQOI2007]涂色paint

      之前做过,还是很容易做得出来

      区间DP,转移的话

      f[i][j]表示i到j染成目标木板的i到j的最少涂色次数

      转移方程有两个:

      1.当st[i]==st[j]时,f[i][j]=min(min(f[i][j-1],f[i+1][j]),f[i+1][j-1]+1),因为如果左右两端都为同样颜色,那么第一次就将整块木板涂成同样颜色即可以做到

      2.常规找断点,f[i][j]=min(f[i][j],f[i][k]+f[k+1][j])

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdlib>
    using namespace std;
    int f[51][51];
    char st[51];
    int main()
    {
        scanf("%s",st+1);int n=strlen(st+1);
        memset(f,63,sizeof(f));
        for(int i=1;i<=n;i++) f[i][i]=1;
        for(int k=2;k<=n;k++)
        {
            for(int i=1;i+k-1<=n;i++)
            {
                if(st[i]==st[i+k-1]) f[i][i+k-1]=min(min(f[i][i+k-2],f[i+1][i+k-1]),f[i+1][i+k-2]+1);
                for(int j=i;j<i+k-1;j++)
                {
                    f[i][i+k-1]=min(f[i][i+k-1],f[i][j]+f[j+1][i+k-1]);
                }
            }
        }
        printf("%d
    ",f[1][n]);
        return 0;
    }
    BZOJ1260

    BZOJ1296: [SCOI2009]粉刷匠

      一开始看错题了,还以为可以竖着粉刷

      先枚举每一块木板,然后在这块木板上求f[i][j],表示这块木板上前i个格子粉刷j次所能得到的最多正确数

      然后对ans[i]表示所有木板粉刷i次所能得到的最多正确数,把f[i][j]插入ans数组中做背包就行了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #define INF 1<<30
    using namespace std;
    char st[51][51];
    int f[51][51];//f[i][j]表示当前木板的前i个刷k次所能得到的最多正确数
    int mx[51];
    int sum[51];
    int ans[3100];//ans[i]表示所有木板粉刷i次最多正确数 
    int main()
    {
        int n,m,T;
        scanf("%d%d%d",&n,&m,&T);
        for(int i=1;i<=n;i++) scanf("%s",st[i]+1);
        memset(ans,-63,sizeof(ans));
        ans[0]=0;int s=0;
        for(int p=1;p<=n;p++)
        {
            memset(f,-63,sizeof(f));
            sum[0]=0;
            for(int i=1;i<=m;i++) sum[i]=sum[i-1]+st[p][i]-'0';
            for(int i=0;i<=m;i++) f[i][0]=0;
            for(int i=1;i<=min(m,T);i++)
            {
                mx[i]=-(1<<30);
                for(int j=i;j<=m;j++)
                {
                    for(int k=i-1;k<j;k++)
                    {
                        if(f[k][i-1]==f[0][1]) continue;
                        f[j][i]=max(f[j][i],f[k][i-1]+max(j-k-sum[j]+sum[k],sum[j]-sum[k]));
                    }
                    mx[i]=max(f[j][i],mx[i]);
                }
            }
            for(int j=T;j>=1;j--)
            {
                for(int i=1;i<=min(j,m);i++)
                {
                    if(ans[j-i]!=ans[T+1]) ans[j]=max(ans[j-i]+mx[i],ans[j]),s=max(ans[j],s);
                }
            }
        }
        printf("%d
    ",s);
        return 0;
    }
    BZOJ1296

    BZOJ1560: [JSOI2009]火星藏宝图

      结论贪心+DP

      因为每个点的固定收益为正,而且两点的距离是欧几里得距离的平方,所以可以得到一个结论:

      假设要从A->C,如果能A->B->C,肯定A->B->C是更优的

      那我们就将点先按x为第一关键字,y为第二关键字排序

      设f[i]为从第1个点到第i个点的最大收益,显然我们转移的时候只需找到前i-1个点所在每一列的最下面的点就可以了

      这样复杂度就是O(mn),感觉卡过去了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    struct sit{int x,y,v;}p[210000];
    bool cmp(sit n1,sit n2){return n1.x==n2.x?n1.y<n2.y:n1.x<n2.x;}
    int f[210000],pos[1100];
    int dis(sit n1,sit n2){return (n1.x-n2.x)*(n1.x-n2.x)+(n1.y-n2.y)*(n1.y-n2.y);}
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].v);
        sort(p+1,p+n+1,cmp);
        memset(f,-63,sizeof(f));
        f[1]=p[1].v;pos[1]=1;
        int x=1;
        for(int i=2;i<=n;i++)
        {
            for(int j=1;j<=p[i].y;j++)
            {
                if(pos[j]!=0)
                {
                    f[i]=max(f[i],f[pos[j]]-dis(p[pos[j]],p[i]));
                }
            }
            f[i]+=p[i].v;
            pos[p[i].y]=i;
        }
        printf("%d
    ",f[n]);
        return 0;
    }
    BZOJ1560

    BZOJ1806: [Ioi2007]Miners 矿工配餐

      直接DP就好了

      设f[i][t1][t2][t3][t4]为当前已经送了i个食物,且最近两次给第一个矿洞送了第t1和t2种食物(t1比t2更早送),给第二个矿洞送了第t3和t4种食物(t3比t4更早送)能得到的最大收益

      假如t1,t2,t3,t4为0,表示当前没有送那么多的餐

      转移想想就会了

      然后这题卡空间。。没关系,滚动数组一下就好了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    char st[110000];
    int f[2][4][4][4][4];
    int a[110000];
    int main()
    {
        int n;
        scanf("%d",&n);
        scanf("%s",st+1);
        for(int i=1;i<=n;i++)
        {
            if(st[i]=='M') a[i]=1;
            if(st[i]=='F') a[i]=2;
            if(st[i]=='B') a[i]=3;
        }
        memset(f,-1,sizeof(f));
        f[0][0][0][0][0]=0;
        int now=0,last=1;
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            now^=1;last^=1;
            for(int t1=0;t1<=3;t1++)
            {
                for(int t2=0;t2<=3;t2++)
                {
                    int d1=0;
                    if(t1!=0&&t2==0)
                    {
                        d1=1;if(t1!=a[i]) d1=2;
                    }
                    else if(t1==0&&t2!=0)
                    {
                        d1=1;if(t2!=a[i]) d1=2;
                    }
                    else if(t1!=0&&t2!=0)
                    {
                        if(t1==t2)
                        {
                            d1=1;if(t1!=a[i]) d1=2;
                        }
                        else
                        {
                            d1=2;if(t1!=a[i]&&t2!=a[i]) d1=3;
                        }
                    }
                    else d1=1;
                    for(int t3=0;t3<=3;t3++)
                    {
                        for(int t4=0;t4<=3;t4++)
                        {
                            int d2=0;
                            if(t3!=0&&t4==0)
                            {
                                d2=1;if(t4!=a[i]) d2=2;
                            }
                            else if(t3==0&&t4!=0)
                            {
                                d2=1;if(t4!=a[i]) d2=2;
                            }
                            else if(t3!=0&&t4!=0)
                            {
                                if(t3==t4)
                                {
                                    d2=1;if(t3!=a[i]) d2=2;
                                }
                                else
                                {
                                    d2=2;if(t3!=a[i]&&t4!=a[i]) d2=3;
                                }
                            }
                            else d2=1;
                            if(f[last][t1][t2][t3][t4]!=-1)
                            {
                                f[now][t2][a[i]][t3][t4]=max(f[now][t2][a[i]][t3][t4],f[last][t1][t2][t3][t4]+d1);
                                f[now][t1][t2][t4][a[i]]=max(f[now][t1][t2][t4][a[i]],f[last][t1][t2][t3][t4]+d2);
                                ans=max(ans,max(f[now][t2][a[i]][t3][t4],f[now][t1][t2][t4][a[i]]));
                            }
                        }
                    }
                }
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    BZOJ1806

    BZOJ2121: 字符串游戏

      这个DP有点神了。。

      因为最后选择删除的多个区间,要么是包含关系,要么就相离关系,不可能存在区间相交的情况

      所以设f1[l][r][i][j]为l到r所构成的字符串删掉若干区间后是否能够刚好构成第i个子串的前j个字符

      f2[l][r]表示能否将l到r所构成的字符串都删掉

      转移就要么就往后加一个字符,要么就找前面后面是否能够继承就行了

      O(|L|3*|S|*|p|),竟然是能过的复杂度。。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define mes(x,y) memset(x,y,sizeof(x))
    using namespace std;
    bool f1[210][210][41][31],f2[210][210];
    char st[210];
    char cc[41][31];
    int h[151],len[210];
    int main()
    {
        scanf("%s",st+1);
        len[0]=strlen(st+1);
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%s",cc[i]+1),len[i]=strlen(cc[i]+1);
        mes(f1,false);mes(f2,false);
        for(int i=1;i<=len[0];i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(cc[j][1]==st[i]) f1[i][i][j][1]=true;
                if(f1[i][i][j][len[j]]==true) f2[i][i]=true;
            }
        }
        for(int k=2;k<=len[0];k++)
        {
            for(int l=1;l<=len[0]-k+1;l++)
            {
                int r=l+k-1;
                for(int i=1;i<=n;i++)
                {
                    for(int j=1;j<=min(len[i],k);j++)
                    {
                        if(st[r]==cc[i][j]) f1[l][r][i][j]|=f1[l][r-1][i][j-1];
                        for(int p=l;p<=r;p++)
                        {
                            if(f2[l][p]==true) f1[l][r][i][j]|=f1[p+1][r][i][j];
                            if(f2[p][r]==true) f1[l][r][i][j]|=f1[l][p-1][i][j];
                        }
                    }
                    if(f1[l][r][i][len[i]]==true) f2[l][r]=true;
                }
            }
        }
        mes(h,63);h[0]=0;
        for(int i=1;i<=len[0];i++)
        {
            h[i]=h[i-1]+1;
            for(int j=1;j<=i;j++) if(f2[j][i]==true) h[i]=min(h[i],h[j-1]);
        }
        printf("%d
    ",h[len[0]]);
        return 0;
    }
    BZOJ2121

    BZOJ2302: [HAOI2011]Problem c

      其实想想就会发现与顺序是没有什么关系的

      因为当一种方案成立,肯定会使得所有编号<=i的人数会>=i,这个想想应该就能懂

      然后就可以写DP方程,设f[i][j]为有j个人的编号<=i的方案数

      转移就组合数一下就行了,对于无解的情况就先判断掉

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define INF 1<<30
    #define mes(x,y) memset(x,y,sizeof(x))
    #define Maxn 310
    using namespace std;
    typedef long long LL;
    LL Mod;
    LL mul(LL a,LL b){return a*b%Mod;}
    LL add(LL a,LL b){return (a+b)%Mod;}
    LL p_mod(LL a,LL b)
    {
        LL ans=1;
        while(b!=0)
        {
            if(b%2==1) ans=mul(ans,a);
            a=mul(a,a);b>>=1;
        }
        return ans;
    }
    int s[Maxn];
    LL f[Maxn][Maxn];//f[i][j]表示有j个人的编号<=i的方案数,显然j>=i
    LL C[Maxn][Maxn];
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n,m;scanf("%d%d%lld",&n,&m,&Mod);
            mes(C,0);C[0][0]=1;
            for(int i=1;i<=300;i++)
            {
                C[i][0]=1;
                for(int j=1;j<=i;j++)
                {
                    C[i][j]=add(C[i-1][j-1],C[i-1][j]);
                }
            }
            mes(s,0);s[0]=n-m;
            int p,q;
            for(int i=1;i<=m;i++) scanf("%d%d",&p,&q),s[q]++;
            bool bk=false;
            for(int i=1;i<=n;i++)
            {
                s[i]+=s[i-1];
                if(s[i]<i){bk=true;break;}
            }
            if(bk==true){printf("NO
    ");continue;}
            printf("YES ");
            mes(f,0);f[0][0]=1;
            for(int i=1;i<=n;i++)
            {
                for(int j=i;j<=s[i];j++)
                {
                    int d=s[i]-s[i-1];
                    for(int k=d;k<=j-i+1;k++)
                    {
                        f[i][j]=add(f[i][j],mul(f[i-1][j-k],C[s[i]-d-j+k][k-d]));
                    }
                }
            }
            printf("%lld
    ",f[n][n]);
        }
        return 0;
    }
    BZOJ2302

    BZOJ2466: [中山市选2009]树

      这个树形DP还是很好想的

      设f[x][i]表示第x个点为i状态时所需要的最少操作数

      对于状态0表示没按按钮且没亮,1表示没按按钮且亮了,2表示按了按钮且没亮,3表示按了按钮且亮了

      然后在保证子节点一定亮的基础上转移就好了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<algorithm>
    #define mes(x,y) memset(x,y,sizeof(x))
    #define Maxn 110
    #define INF 1<<30
    using namespace std;
    typedef long long LL;
    struct node{int x,y,next;}a[Maxn*2];int len,last[Maxn];
    void ins(int x,int y){a[++len]=(node){x,y,last[x]};last[x]=len;}
    int f[Maxn][5],n;
    void dfs(int x,int fa)
    {
        int f1[4];f[x][0]=0;f[x][3]=1;f[x][1]=f[x][2]=n+1;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y==fa) continue;
            dfs(y,x);
            f1[0]=f[x][0];f1[1]=f[x][1];f1[2]=f[x][2];f1[3]=f[x][3];
            f[x][0]=min(f1[0]+f[y][1],f1[1]+f[y][3]);
            f[x][1]=min(f1[1]+f[y][1],f1[0]+f[y][3]);
            f[x][2]=min(f1[2]+f[y][0],f1[3]+f[y][2]);
            f[x][3]=min(f1[3]+f[y][0],f1[2]+f[y][2]);
        }
    }
    int main()
    {
        while(scanf("%d",&n)!=EOF)
        {
            if(n==0) break;
            int x,y;
            len=0;mes(last,0);
            for(int i=1;i<n;i++) scanf("%d%d",&x,&y),ins(x,y),ins(y,x);
            dfs(1,0);
            printf("%d
    ",min(f[1][1],f[1][3]));
        }
        return 0;
    }
    BZOJ2466

    BZOJ1787: [Ahoi2008]Meet 紧急集合

      做过

      转化为求三个点的LCA

      发现三个点两两之间的LCA中必定有两个相同,而且其中不同的那个就是三个点的LCA

      然后直接做就好了

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdlib>
    using namespace std;
    struct node
    {
        int x,y,next;
    }a[1100000];int len,last[510000];
    int f[510000][25],dep[510000];
    void ins(int x,int y)
    {
        len++;
        a[len].x=x;a[len].y=y;
        a[len].next=last[x];last[x]=len;
    }
    int bin[25];
    void dfs(int x,int fa)
    {
        dep[x]=dep[fa]+1;
        f[x][0]=fa;
        for(int i=1;bin[i]<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y==fa) continue;
            dfs(y,x);
        }
    }
    int LCA(int x, int y)
    {
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=20;i>=0;i--) if(dep[x]-dep[y]>=bin[i]) x=f[x][i];
        if(x==y) return x;
        for(int i=20;i>=0;i--) if(dep[x]>=(1<<i)&&f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    int n,m;
    int dis(int x,int y)
    {
        int lca=LCA(x,y);
        return dep[x]+dep[y]-2*dep[lca];
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        bin[0]=1;
        for(int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
        len=0;memset(last,0,sizeof(last));
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            ins(x,y);ins(y,x);
        }
        dfs(1,0);
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            int l1=LCA(x,y),l2=LCA(x,z),l3=LCA(y,z);
            if(l1==l2) printf("%d %d
    ",l3,dis(x,l3)+dis(y,l3)+dis(z,l3));
            else if(l1==l3) printf("%d %d
    ",l2,dis(x,l2)+dis(y,l2)+dis(z,l2));
            else if(l2==l3) printf("%d %d
    ",l1,dis(x,l1)+dis(y,l1)+dis(z,l1));
        }
        return 0;
    }
    BZOJ1787

    BZOJ1202: [HNOI2005]狡猾的商人

      带权并查集就好了,判断是否合法

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int fa[110],v[110];
    int findfa(int x)
    {
        if(fa[x]==x) return x;
        int f=fa[x];
        fa[x]=findfa(fa[x]);
        v[x]+=v[f];
        return fa[x];
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            memset(v,0,sizeof(v));
            int n,m;
            scanf("%d%d",&n,&m);
            for(int i=0;i<=n;i++) fa[i]=i;
            bool bk=true;
            for(int i=1;i<=m;i++)
            {
                int x,y,k;
                scanf("%d%d%d",&x,&y,&k);
                if(bk==false) continue;
                int fx=findfa(x-1),fy=findfa(y);
                if(fx==fy)
                {
                    if(v[y]-v[x-1]!=k)
                    {
                        bk=false;continue;
                    }
                }
                else
                {
                    fa[fy]=fx;
                    v[fy]=k-v[y]+v[x-1];
                }
            }
            if(bk==false) printf("false
    ");
            else printf("true
    ");
        }
        return 0;
    }
    BZOJ1202

    BZOJ2006: [NOI2010]超级钢琴

      实际上就是求前k大子段和

      暴力做就是枚举每个左端点然后枚举右端点

      然而我们可以预处理前缀和,用RMQ就能够得到每个点为左端点时的最优右端点

      可以发现以当前点为左端点的次优点一定不是右端点

      那么我们就用优先队列一个个取,设四元组(p,x,y,d)为当前以p点为左端点,在x到y中选择最优右端点,得到的最大子段和为d

      每次取出队顶的时候,将队顶分为(p,x,j-1,d1),(p,j+1,y,d2)j为以p点为左端点,在x到y中的最优右端点

      而d1,d2就不用说了,就这样取k次就好了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #define mes(x,y) memset(x,y,sizeof(x))
    #define Maxn 510000
    using namespace std;
    typedef long long LL;
    struct node
    {
        int p,l,r;LL d;
        friend bool operator < (node n1,node n2){return n1.d<n2.d;}
    };
    priority_queue<node> q;
    LL a[Maxn],f[Maxn][21];
    int Log[Maxn],p[Maxn][21];
    LL mx(int l,int r)
    {
        int t=Log[r-l+1];
        return max(f[l][t],f[r-(1<<t)+1][t]);
    }
    int P(int l,int r)
    {
        int t=Log[r-l+1];
        if(f[l][t]>f[r-(1<<t)+1][t]) return p[l][t];
        else return p[r-(1<<t)+1][t];
    }
    int main()
    {
        int n,k,L,R;
        scanf("%d%d%d%d",&n,&k,&L,&R);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]),a[i]+=a[i-1],f[i][0]=a[i],p[i][0]=i;
        Log[0]=-1;for(int i=1;i<=n;i++) Log[i]=Log[i/2]+1;
        for(int i=1;i<=20;i++)
        {
            for(int j=1;j<=n-(1<<i)+1;j++)
            {
                if(f[j][i-1]>f[j+(1<<(i-1))][i-1]) f[j][i]=f[j][i-1],p[j][i]=p[j][i-1];
                else f[j][i]=f[j+(1<<(i-1))][i-1],p[j][i]=p[j+(1<<(i-1))][i-1];
            }
        }
        for(int i=1;i<=n-L+1;i++)
        {
            int x=i+L-1,y=min(i+R-1,n);
            q.push((node){i,x,y,mx(x,y)-a[i-1]});
        }
        LL ans=0;
        for(int i=1;i<=k;i++)
        {
            node tno=q.top();q.pop();
            int p=P(tno.l,tno.r);
            if(tno.l<=p-1) q.push((node){tno.p,tno.l,p-1,mx(tno.l,p-1)-a[tno.p-1]});
            if(p+1<=tno.r) q.push((node){tno.p,p+1,tno.r,mx(p+1,tno.r)-a[tno.p-1]});
            ans+=tno.d;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    BZOJ2006

    BZOJ2957: 楼房重建

      对于一个楼房被看到,可以看作它与(0,0)的连线的斜率比前面所有的斜率都大

      那么我们可以把每次操作都当成单点修改,然后求整段区间从第一个楼房开始斜率递增所得到的楼房数

      设mx为每个区间中最大的斜率,c为每个区间从左端点开始能看到的楼房数

      显然我们需要在修改的时候维护线段树

      对于一段区间,显然左子区间的c值的贡献一定全部在整段区间的c值中,所以我们只要对右子区间进行处理

      如果当前左子区间的最大值>=右子区间的最大值,那么右子区间对整段区间是没有贡献的

      不然则在右子区间中找出以第一个大于左子区间的最大值的楼房,然后贡献为从这个楼房往后得到的楼房数(包括这个楼房)

      就这样,注意一下精度就可以了

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdlib>
    #define eps 1e-10
    using namespace std;
    struct trnode
    {
        int l,r,lc,rc,c;
        double mx;
    }tr[210000];int trlen;
    void bt(int l,int r)
    {
        int now=++trlen;
        tr[now].l=l;tr[now].r=r;
        tr[now].lc=tr[now].rc=-1;
        tr[now].c=0;tr[now].mx=0.0;
        if(l<r)
        {
            int mid=(l+r)/2;
            tr[now].lc=trlen+1;bt(l,mid);
            tr[now].rc=trlen+1;bt(mid+1,r);
        }
    }
    int ans;
    void findd(int now,double d)
    {
        if(tr[now].l==tr[now].r){ans++;return ;}
        int lc=tr[now].lc,rc=tr[now].rc;
        if(tr[lc].mx<=d) findd(rc,d);
        else ans+=tr[now].c-tr[lc].c,findd(lc,d);
    }
    void follow(int now)
    {
        int lc=tr[now].lc,rc=tr[now].rc;
        tr[now].c=tr[lc].c;tr[now].mx=tr[lc].mx;
        if(tr[rc].mx-tr[lc].mx>eps)
        {
            tr[now].mx=tr[rc].mx;
            ans=0;findd(rc,tr[lc].mx);
            tr[now].c+=ans;
        }
    }
    void change(int now,int x,double d)
    {
        if(tr[now].l==tr[now].r)
        {
            tr[now].mx=d;
            tr[now].c=1;
            return ;
        }
        int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2;
        if(x<=mid) change(lc,x,d);
        else change(rc,x,d);
        follow(now);
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        trlen=0;bt(1,n);
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            change(1,x,double(y)/double(x));
            printf("%d
    ",tr[1].c);
        }
        return 0;
    }
    BZOJ2957

    BZOJ3289: Mato的文件管理

      莫队,考虑加数减数时,树状数组求逆序对数对答案的影响即可

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    struct LS
    {
        int d,id;
    }s[51000];
    struct qn
    {
        int l,r,id;
        LL d;
    }q[51000];
    int bk[51000];
    bool cmp(qn n1,qn n2)
    {
        return bk[n1.l]==bk[n2.l]?n1.r<n2.r:bk[n1.l]<bk[n2.l];
    }
    bool cmpd(qn n1,qn n2)
    {
        return n1.id<n2.id;
    }
    bool lsd(LS n1,LS n2)
    {
        return n1.d<n2.d;
    }
    bool lsid(LS n1,LS n2)
    {
        return n1.id<n2.id;
    }
    LL a[51000];
    int lowbit(int x){return x&-x;}
    int n;
    LL getsum(int x)
    {
        LL ans=0;
        while(x!=0)
        {
            ans+=a[x];
            x-=lowbit(x);
        }
        return ans;
    }
    void change(int x,LL c)
    {
        while(x<=n)
        {
            a[x]+=c;
            x+=lowbit(x);
        }
    }
    LL t[51000];
    int main()
    {
        scanf("%d",&n);
        int block=int(sqrt(n));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&s[i].d);
            s[i].id=i;
            bk[i]=(i-1)/block+1;
        }
        sort(s+1,s+n+1,lsd);
        for(int i=1;i<=n;i++) s[i].d=i;
        sort(s+1,s+n+1,lsid);
        int m;
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
        }
        sort(q+1,q+m+1,cmp);
        int l=1,r=0;
        LL ans=0;
        memset(a,0,sizeof(a));
        for(int i=1;i<=m;i++)
        {
            while(l<q[i].l)
            {
                if(l>r){l++;continue;}
                ans-=(r-l+1)-getsum(s[l].d);
                change(1,-1);
                if(s[l].d!=n) change(s[l].d+1,1);
                l++;
            }
            while(l>q[i].l)
            {
                if(l>r+1){l--;continue;}
                ans+=(r-l+1)-getsum(s[l-1].d);
                l--;
                change(1,1);
                if(s[l].d!=n) change(s[l].d+1,-1);
            }
            while(r<q[i].r)
            {
                if(l>r+1){r++;continue;}
                ans+=getsum(s[r+1].d);
                r++;
                change(1,1);
                if(s[r].d!=n) change(s[r].d+1,-1);
            }
            while(r>q[i].r)
            {
                if(l>r){r--;continue;}
                ans-=getsum(s[r].d)-1;
                change(1,-1);
                if(s[r].d!=n) change(s[r].d+1,1);
                r--;
            }
            q[i].d=ans;
        }
        sort(q+1,q+m+1,cmpd);
        for(int i=1;i<=m;i++) printf("%lld
    ",q[i].d);
        return 0;
    }
    BZOJ3289

    BZOJ3242: [Noi2013]快餐店

      显然是一棵基环树,而且对于一个最优快餐店的位置而言,它肯定有一条环上的边不会走

      那么就枚举删边,然后在环上操作,求出相当于当前删边后的树的直径,求出最小的

      然后再和原图上原来的子树的直径比较就行了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #define mes(x,y) memset(x,y,sizeof(x))
    #define Maxn 110000
    #define INF 1LL<<62
    using namespace std;
    struct node{int x,y,next;double d;}a[Maxn*2];int len,last[Maxn];
    void ins(int x,int y,double d){a[++len]=(node){x,y,last[x],d};last[x]=len;}
    int dfn[Maxn],id,fa[Maxn];
    int e[Maxn],cnt,p[Maxn];
    bool in[Maxn];
    void ph(int x)
    {
        dfn[x]=++id;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y==a[fa[x]].x) continue;
            if(dfn[y]!=0)
            {
                if(dfn[x]>dfn[y]) continue;
                e[++cnt]=((k-1)^1)+1;p[cnt]=x;in[x]=true;
                while(y!=x) e[++cnt]=fa[y],in[y]=true,p[cnt]=y,y=a[fa[y]].x;
            }
            else fa[y]=k,ph(y);
        }
    }
    double f1[Maxn],f2[Maxn],MX;//f1直径,f2链 
    void dfs(int x,int fa)
    {
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y==fa||in[y]==true) continue;
            dfs(y,x);
            f1[x]=max(f1[x],f2[x]+f2[y]+a[k].d);
            MX=max(MX,f1[x]);
            f2[x]=max(f2[x],f2[y]+a[k].d);
        }
    }
    double s[Maxn];
    double L[Maxn],l[Maxn],R[Maxn],r[Maxn];
    double ml[Maxn],mr[Maxn];
    int to[Maxn];
    int main()
    {
        int n;
        scanf("%d",&n);
        len=0;mes(last,0);
        for(int i=1;i<=n;i++)
        {
            int x,y;double d;
            scanf("%d%d%lf",&x,&y,&d);
            ins(x,y,d);ins(y,x,d);
        }
        mes(in,false);
        cnt=id=0;ph(1);
        mes(f1,0);mes(f2,0);
        for(int i=1;i<=cnt;i++) dfs(p[i],0);
        reverse(e+1,e+cnt+1);
        to[1]=a[e[1]].x;s[1]=a[e[1]].d;
        for(int i=2;i<=cnt;i++) to[i]=a[e[i-1]].y,s[i]=a[e[i]].d;
        to[cnt+1]=to[1];
    //    int head=1,tail=1;list[1]=1;
    //    for(int i=2;i<=cnt*2+1;i++)
    //    {
    //        while(head<=tail&&i-list[head]>=cnt) head++;
    //        int x=list[head];
    //        if(i>=cnt) R[i]=f1[to[i]]+f1[to[x]]+s[i]-s[x];
    //        while(head<=tail&&(f1[to[i]]-s[i])>=(f1[to[list[tail]]]-s[list[tail]])) tail--;
    //        list[++tail]=i;
    //    }
        L[0]=l[0]=0;
        double sum=0,mx=0;
    //    for(int i=1;i<=cnt+1;i++) printf("%d ",to[i]);printf("
    ");
    //    for(int i=1;i<=cnt+1;i++) printf("%.1lf ",f2[to[i]]);printf("
    ");
    //    printf("L
    ");
        for(int i=1;i<=cnt+1;i++)
        {
            sum+=s[i-1];
            L[i]=max(L[i-1],mx+f2[to[i]]+sum);
            l[i]=max(l[i-1],sum+f2[to[i]]);
            mx=max(mx,f2[to[i]]-sum);
    //        printf("%d: L:%.1lf l:%.1lf ml:%.1lf
    ",i,L[i],l[i],ml[i]);
        }
        R[cnt+1]=r[cnt+1]=mx=0;sum=0;
    //    printf("R
    ");
        for(int i=cnt;i>=1;i--)
        {
            sum+=s[i];
            R[i]=max(R[i+1],mx+f2[to[i]]+sum);
            r[i]=max(r[i+1],sum+f2[to[i]]);
            mx=max(mx,f2[to[i]]-sum);
    //        printf("%d: R:%.1lf r:%.1lf mr:%.1lf
    ",i,R[i],r[i],mr[i]);
        }
        double ans=INF;
        for(int i=1;i<=cnt;i++) ans=min(ans,max(max(L[i],R[i+1]),l[i]+r[i+1]));
        printf("%.1lf
    ",max(ans,MX)/2.0);
        return 0;
    }
    BZOJ3242

    BZOJ4170: 极光

      将位置i以及权值d,当作是坐标(x,y),那么每次询问就是求与当前坐标的曼哈顿距离<=k的点数

      显然不好求,那么就转换一下坐标系,将坐标转换为(x+y,x-y),将曼哈顿距离转换为切比雪夫距离

      转换为矩阵之后就是CDQ分治裸题了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define mes(x,y) memset(x,y,sizeof(x))
    #define Maxn 110000
    using namespace std;
    struct ask
    {
        int tp,x,y,t,f;/*type px py anst 1/-1*/
        friend bool operator < (ask n1,ask n2){return n1.x==n2.x?n1.tp<n2.tp:n1.x<n2.x;}
    }Q[Maxn*4],tmp[Maxn*4];//1为修改,2为询问
    int a[Maxn*8];
    int lowbit(int x){return x&-x;}
    void change(int x,int d)
    {
        while(x<=Maxn*4)
        {
            a[x]+=d;
            x+=lowbit(x);
        }
    }
    int getsum(int x)
    {
        int ans=0;
        while(x!=0)
        {
            ans+=a[x];
            x-=lowbit(x);
        }
        return ans;
    }
    void clear(int x)
    {
        while(x!=0)
        {
            if(a[x]==0) break;
            a[x]=0;x+=lowbit(x);
        }
    }
    int ans[Maxn];
    void CDQ(int l,int r)
    {
        if(l==r) return ;
        int mid=(l+r)/2;
        CDQ(l,mid);CDQ(mid+1,r);
        int p=l,q=mid+1,t=l;
        while(p<=mid&&q<=r)
        {
            if(Q[p]<Q[q])
            {
                if(Q[p].tp==1) change(Q[p].y,1);
                tmp[t++]=Q[p++];
            }
            else
            {
                if(Q[q].tp==2) ans[Q[q].t]+=Q[q].f*getsum(Q[q].y);
                tmp[t++]=Q[q++];
            }
        }
        while(p<=mid) tmp[t++]=Q[p++];
        while(q<=r)
        {
            if(Q[q].tp==2) ans[Q[q].t]+=Q[q].f*getsum(Q[q].y);
            tmp[t++]=Q[q++];
        }
        for(int i=l;i<=r;i++)
        {
            if(i<=mid) clear(Q[i].y);
            Q[i]=tmp[i];
        }
    }
    int nx[Maxn],ny[Maxn];
    int main()
    {
        int n,q;
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
        {
            int d;scanf("%d",&d);
            Q[i]=(ask){1,i+d+Maxn,i-d+Maxn,0,0};
            nx[i]=Q[i].x;ny[i]=Q[i].y;
        }
        int cnt=0,nn=n;
        for(int i=1;i<=q;i++)
        {
            char st[7];int x,d;
            scanf("%s%d%d",st+1,&x,&d);
            if(st[1]=='M')
            {
                Q[++n]=(ask){1,x+d+Maxn,x-d+Maxn,0,0};
                nx[x]=Q[n].x;ny[x]=Q[n].y;
            }
            else
            {
                ++cnt;
                Q[++n]=(ask){2,min(Maxn*4,nx[x]+d),min(Maxn*4,ny[x]+d),cnt,1};
                Q[++n]=(ask){2,max(nx[x]-d-1,1),min(Maxn*4,ny[x]+d),cnt,-1};
                Q[++n]=(ask){2,min(Maxn*4,nx[x]+d),max(ny[x]-d-1,1),cnt,-1};
                Q[++n]=(ask){2,max(nx[x]-d-1,1),max(ny[x]-d-1,1),cnt,1};
            }
        }
        mes(a,0);
        CDQ(1,n);
        //for(int i=1;i<=n;i++) printf("%d %d %d %d
    ",tmp[i].tp,tmp[i].x,tmp[i].y,tmp[i].nn);printf("
    ");
        for(int i=1;i<=cnt;i++) printf("%d
    ",ans[i]);
        return 0;
    }
    BZOJ4170

    BZOJ3781: 小B的询问

      莫队即可

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    int a[51000];
    struct qn
    {
        int l,r,id;
        LL d;
    }q[51000];
    int bk[51000];
    bool cmp(qn n1,qn n2)
    {
        return bk[n1.l]==bk[n2.l]?n1.r<n2.r:bk[n1.l]<bk[n2.l];
    }
    bool cmpd(qn n1,qn n2)
    {
        return n1.id<n2.id;
    }
    LL sum[51000],ans;
    void update(int x,int ad)
    {
        ans-=sum[a[x]]*sum[a[x]];
        sum[a[x]]+=ad;
        ans+=sum[a[x]]*sum[a[x]];
    }
    int main()
    {
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        int block=int(sqrt(n));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            bk[i]=(i-1)/block+1;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
        }
        sort(q+1,q+m+1,cmp);
        int l=1,r=0;
        ans=0;
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=m;i++)
        {
            while(l<q[i].l){update(l,-1);l++;}
            while(l>q[i].l){update(l-1,1);l--;}
            while(r<q[i].r){update(r+1,1);r++;}
            while(r>q[i].r){update(r,-1);r--;}
            q[i].d=ans;
        }
        sort(q+1,q+m+1,cmpd);
        for(int i=1;i<=m;i++) printf("%lld
    ",q[i].d);
        return 0;
    }
    BZOJ3781

    BZOJ3809: Gty的二逼妹子序列

      莫队即可

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    struct node
    {
        int l,r,a,b,id,d;
    }q[1100000];
    int s[110000];
    int bl[1100],br[1100],belong[110000];
    bool cmp1(node n1,node n2)
    {
        if(belong[n1.l]<belong[n2.l]) return true;
        if(belong[n1.l]>belong[n2.l]) return false;
        if(belong[n1.l]==belong[n2.l])
        {
            if(n1.r<n2.r) return true;
            if(n1.r>n2.r) return false;
        }
        return false;
    }
    bool cmp2(node n1,node n2)
    {
        return n1.id<n2.id;
    }
    int d[1100],sum[110000];
    void add(int x)
    {
        sum[x]++;
        if(sum[x]==1) d[belong[x]]++;
    }
    void del(int x)
    {
        sum[x]--;
        if(sum[x]==0) d[belong[x]]--;
    }
    int solve(int x,int y)
    {
        int bx=belong[x],by=belong[y];
        int ans=0;
        if(bx==by)
        {
            for(int i=x;i<=y;i++)
            {
                if(sum[i]>0) ans++;
            }
            return ans;
        }
        for(int i=bx+1;i<=by-1;i++) ans+=d[i];
        for(int i=x;i<=br[bx];i++) if(sum[i]>0) ans++;
        for(int i=bl[by];i<=y;i++) if(sum[i]>0) ans++;
        return ans;
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&s[i]);
        int block=int(sqrt(n));
        for(int i=1;i<=n;i++)
        {
            int t=(i-1)/block+1;
            belong[i]=t;
            if(bl[t]==0) br[t-1]=i-1,bl[t]=i;
        }
        br[belong[n]]=n;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].a,&q[i].b);
            q[i].id=i;
        }
        sort(q+1,q+m+1,cmp1);
        memset(sum,0,sizeof(sum));
        memset(d,0,sizeof(d));
        int l=1,r=0;
        for(int i=1;i<=m;i++)
        {
            while(l>q[i].l){l--;add(s[l]);}
            while(l<q[i].l){del(s[l]);l++;}
            while(r>q[i].r){del(s[r]);r--;}
            while(r<q[i].r){r++;add(s[r]);}
            q[i].d=solve(q[i].a,q[i].b);
        }
        sort(q+1,q+m+1,cmp2);
        for(int i=1;i<=m;i++) printf("%d
    ",q[i].d);
        return 0;
    }
    BZOJ3809

    BZOJ3133: [Baltic2013]ballmachine

      还是挺裸的

      可以发现实际上一开始将所有球扔下去之后得到的序列是固定的,每次放球的时候,只要求出序列中最靠前的位置放球就行了

      而且可以一个一个球放,因为输入合法,删的球数最多就是放的球数

      对于删球,实际上求的就是当前位置向上的球数

      直接用两个线段树,再树剖一下就行了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #define mes(x,y) memset(x,y,sizeof(x))
    #define Maxn 110000
    using namespace std;
    vector<int> a[Maxn];
    void ins(int x,int y){a[x].push_back(y);}
    int pt[Maxn],id,pre[Maxn],to[Maxn];
    bool cmp(int x,int y){return pt[x]<pt[y];}
    struct node{int l,r,lc,rc,c;}tr[Maxn*2],p[Maxn*2];int trlen,plen;
    void bt(int t,int l,int r)
    {
        if(t==0)
        {
            int now=++trlen;
            tr[now]=(node){l,r,-1,-1,0};
            if(l<r)
            {
                int mid=(l+r)/2;
                tr[now].lc=trlen+1;bt(0,l,mid);
                tr[now].rc=trlen+1;bt(0,mid+1,r);
            }
        }
        else
        {
            int now=++plen;
            p[now]=(node){l,r,-1,-1,0};
            if(l<r)
            {
                int mid=(l+r)/2;
                p[now].lc=plen+1;bt(1,l,mid);
                p[now].rc=plen+1;bt(1,mid+1,r);
            }
        }
    }
    int getspace(int now)
    {
        if(tr[now].l==tr[now].r) return tr[now].l;
        int lc=tr[now].lc,rc=tr[now].rc;
        if(tr[lc].r-tr[lc].l+1>tr[lc].c) return getspace(lc);
        else return getspace(rc);
    }
    void inout(int now,int x,int d)
    {
        if(tr[now].l==tr[now].r){tr[now].c=d;return ;}
        int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2;
        if(x<=mid) inout(lc,x,d);
        else inout(rc,x,d);
        tr[now].c=tr[lc].c+tr[rc].c;
    }
    int tot[Maxn],son[Maxn],fa[Maxn],dep[Maxn];
    void dfs1(int x)//pre_tree_node
    {
        son[x]=0;tot[x]=1;
        for(int i=0;i<a[x].size();i++)
        {
            int y=a[x][i];
            fa[y]=x;dep[y]=dep[x]+1;
            dfs1(y);
            tot[x]+=tot[y];
            if(tot[y]>tot[son[x]]) son[x]=y;
            pt[x]=min(pt[x],pt[y]);
        }
    }
    void dfs2(int x)
    {
        sort(a[x].begin(),a[x].end(),cmp);
        for(int i=0;i<a[x].size();i++)
        {
            int y=a[x][i];
            dfs2(y);
        }
        pre[++id]=x;to[x]=id;
    }
    int top[Maxn],ys[Maxn],z,bk[Maxn];
    void pre_tree_edge(int x,int tp)
    {
        ys[x]=++z;top[x]=tp;bk[z]=x;
        if(son[x]!=0) pre_tree_edge(son[x],tp);
        for(int i=0;i<a[x].size();i++)
        {
            int y=a[x][i];
            if(y!=son[x]) pre_tree_edge(y,y);
        }
    }
    void change(int now,int x,int d)
    {
        if(p[now].l==p[now].r){p[now].c=d;return ;}
        int lc=p[now].lc,rc=p[now].rc,mid=(p[now].l+p[now].r)/2;
        if(x<=mid) change(lc,x,d);
        else change(rc,x,d);
        p[now].c=p[lc].c+p[rc].c;
    }
    int getsum(int now,int l,int r)
    {
        if(p[now].l==l&&p[now].r==r) return p[now].c;
        int lc=p[now].lc,rc=p[now].rc,mid=(p[now].l+p[now].r)/2;
        if(r<=mid) return getsum(lc,l,r);
        else if(l>mid) return getsum(rc,l,r);
        else return getsum(lc,l,mid)+getsum(rc,mid+1,r);
    }
    int sit,rt;
    int getsit(int x)
    {
        int tx=top[x],ans=0,pp;
        while(x!=0)
        {
            int d=getsum(1,ys[tx],ys[x]);
            ans+=d;
            if(d==ys[x]-ys[tx]+1) pp=tx,x=fa[tx],tx=top[x];
            else
            {
                if(d!=0) sit=bk[ys[x]-d+1];
                else sit=pp;
                return ans-1;
            }
        }
        sit=rt;
        return ans-1;
    }
    int main()
    {
        int n,Q;
        scanf("%d%d",&n,&Q);
        for(int i=1;i<=n;i++)
        {
            int f;
            scanf("%d",&f);
            if(f==0) rt=i;
            else ins(f,i);
        }
        for(int i=1;i<=n;i++) pt[i]=i;
        fa[rt]=dep[rt]=0;dfs1(rt);
        id=0;dfs2(rt);
        z=0;pre_tree_edge(rt,rt);
        trlen=0;bt(0,1,n);
        plen=0;bt(1,1,z);
        while(Q--)
        {
            int op,x,sp;
            scanf("%d%d",&op,&x);
            if(op==1)
            {
                for(int i=1;i<=x;i++)
                {
                    sp=getspace(1);
                    inout(1,sp,1);
                    change(1,ys[pre[sp]],1);
                    if(i==x) printf("%d
    ",pre[sp]);
                }
            }
            else
            {
                printf("%d
    ",getsit(x));
                inout(1,to[sit],0);
                change(1,ys[sit],0);
            }
        }
        return 0;
    }
    BZOJ3133

    BZOJ4662: Snow

      有个数组开小了,成功拍了两个下午,真开心。。

      因为区间互不包含,而且左端点递增,所以右端点也是递增的

      那么就先离散化,将所有都分成一段一段的

      然后线段树维护每个人的清理区域,因为每一小段影响的人的编号也是一个区间

      然后每次找到最小的人就暴力扫一遍这个人清理区间的所有小段,逐一修改

      用并查集来加快扫,保证每个小段只会被扫一次就行了

      一直以为自己处理每一小段的影响时有细节没做好才导致WA,结果是因为有一个数组没开好(怪不得小数据拍不出来

    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #define Maxn 310000
    #define mes(x,y) memset(x,y,sizeof(x))
    #define INF 1LL<<62
    using namespace std;
    typedef long long LL;
    struct LS{int x,z,id;}A[Maxn*4];
    bool cmp1(LS n1,LS n2){return n1.x<n2.x;}
    bool cmp2(LS n1,LS n2){return n1.id<n2.id;}
    int to[Maxn*2];
    struct node{int l,r,lc,rc,p;LL lz,c;}tr[Maxn*4];int trlen;
    int lx(int x){return A[2*x-1].x;}
    int rx(int x){return A[2*x].x;}
    int lz(int x){return A[2*x-1].z;}
    int rz(int x){return A[2*x].z;}
    void bt(int l,int r)
    {
        int now=++trlen;
        tr[now]=(node){l,r,-1,-1,0,0,INF};
        if(l==r) tr[now].c=rx(l)-lx(l),tr[now].p=l;
        else
        {
            int mid=(l+r)/2;
            tr[now].lc=trlen+1;bt(l,mid);
            tr[now].rc=trlen+1;bt(mid+1,r);
            int lc=tr[now].lc,rc=tr[now].rc;
            tr[now].c=min(tr[lc].c,tr[rc].c);
            tr[now].p=tr[lc].c<=tr[rc].c?tr[lc].p:tr[rc].p;
        }
    }
    void update(int now)
    {
        int lc=tr[now].lc,rc=tr[now].rc;
        if(lc!=0)
        {
            tr[lc].c+=tr[now].lz;
            tr[lc].lz+=tr[now].lz;
        }
        if(rc!=0)
        {
            tr[rc].c+=tr[now].lz;
            tr[rc].lz+=tr[now].lz;
        }
        tr[now].lz=0;
    }
    void change(int now,int l,int r,LL c)
    {
        if(tr[now].l==l&&tr[now].r==r)
        {
            tr[now].c+=c;
            tr[now].lz+=c;
            return ;
        }
        int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2;
        if(tr[now].lz!=0) update(now);
        if(r<=mid) change(lc,l,r,c);
        else if(l>mid) change(rc,l,r,c);
        else change(lc,l,mid,c),change(rc,mid+1,r,c);
        tr[now].c=min(tr[lc].c,tr[rc].c);
        tr[now].p=tr[lc].c<=tr[rc].c?tr[lc].p:tr[rc].p;
    }
    int fa[Maxn*4];
    int findfa(int x)
    {
        if(fa[x]!=x) fa[x]=findfa(fa[x]);
        return fa[x];
    }
    int L[Maxn*4],R[Maxn*4];
    int main()
    {
    //    freopen("4662.in","r",stdin);
    //    freopen("4662.out","w",stdout);
        int t,n;
        scanf("%d%d",&t,&n);
        for(int i=1;i<=n;i++) scanf("%d%d",&A[2*i-1].x,&A[2*i].x),A[2*i-1].id=2*i-1,A[2*i].id=2*i;
        sort(A+1,A+2*n+1,cmp1);
        int z=1;A[1].z=1;to[1]=A[1].x;
        for(int i=2;i<=2*n;i++)
        {
            if(A[i].x!=A[i-1].x) z++;
            A[i].z=z;to[z]=A[i].x;
        }
        sort(A+1,A+2*n+1,cmp2);
        trlen=0;bt(1,n);
        int p=1;
        for(int i=2;i<=z;i++)
        {
            if(lz(p)<=i-1&&i<=rz(p)) L[i]=p;
            else
            {
                while(p<=n&&(!(lz(p)<=i-1&&i<=rz(p))))
                {
                    p++;
                    if(lz(p)>=i) break;
                }
                if(!(lz(p)<=i-1&&i<=rz(p))) L[i]=z+1;
                else L[i]=p;
            }
        }
        p=n;
        for(int i=z;i>=2;i--)
        {
            if(lz(p)<=i-1&&i<=rz(p)) R[i]=p;
            else
            {
                while(p>=1&&(!(lz(p)<=i-1&&i<=rz(p))))
                {
                    p--;
                    if(rz(p)<=i-1) break;
                }
                if(!(lz(p)<=i-1&&i<=rz(p))) R[i]=0;
                else R[i]=p;
            }
        }
        for(int i=1;i<=z+1;i++) fa[i]=i;
        for(int i=1;i<=n;i++)
        {
            int sit=tr[1].p;printf("%d
    ",sit);
    //        if(tr[1].c<0)
    //        {
    //            printf("WA
    ");
    //            return 0;
    //        }
            int x=findfa(lz(sit)+1);
            while(x<=rz(sit))
            {
                if(L[x]<=R[x]) change(1,L[x],R[x],-to[x]+to[x-1]);
                int y=findfa(x+1);fa[x]=y;
                x=y;
            }
            change(1,sit,sit,INF);
        }
        return 0;
    }
    BZOJ4662

    BZOJ1237: [SCOI2008]配对

      贪心+DP即可

      先将两个数组排序

      随便画画图就大概可以知道,配对的两个数相对位置不会>2

      所以设f[i]为配i对数的最小值,然后从i-2,i-1,i继承就行了

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define Maxn 110000
    #define mes(x,y) memset(x,y,sizeof(x))
    using namespace std;
    typedef long long LL;
    int A[Maxn],B[Maxn];
    LL f[Maxn];
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d%d",&A[i],&B[i]);
        sort(A+1,A+n+1);sort(B+1,B+n+1);
        if(n==1&&A[1]==B[1]){printf("-1
    ");return 0;}
        memset(f,63,sizeof(f));f[0]=0;
        for(int i=1;i<=n;i++)
        {
            if(A[i]!=B[i]) f[i]=min(f[i],f[i-1]+abs(A[i]-B[i]));
            if(i>=2&&A[i-1]!=B[i]&&A[i]!=B[i-1]) f[i]=min(f[i],f[i-2]+abs(A[i-1]-B[i])+abs(A[i]-B[i-1]));
            if(i>=3)
            {
                //i-2-->i-1 i-1-->i i-->i-2
                if(A[i-2]!=B[i-1]&&A[i-1]!=B[i]&&A[i]!=B[i-2]) f[i]=min(f[i],f[i-3]+abs(A[i-2]-B[i-1])+abs(A[i-1]-B[i])+abs(A[i]-B[i-2]));
                //i-1-->i-2 i-->i-1 i-2-->i
                if(A[i-1]!=B[i-2]&&A[i]!=B[i-1]&&A[i-2]!=B[i]) f[i]=min(f[i],f[i-3]+abs(A[i-1]-B[i-2])+abs(A[i]-B[i-1])+abs(A[i-2]-B[i]));
                //i-2-->i i-1-->i-1 i-->i-2
                if(A[i-2]!=B[i]&&A[i-1]!=B[i-1]&&A[i]!=B[i-2]) f[i]=min(f[i],f[i-3]+abs(A[i-2]-B[i])+abs(A[i-1]-B[i-1])+abs(A[i]-B[i-2]));
            }
        }
        printf("%lld
    ",f[n]);
        return 0;
    }
    BZOJ1237

    BZOJ1221: [HNOI2001] 软件开发

      看题就很像费用流

      这题关键在于毛巾能够重复用

      现将每天拆成两个点,一个入点,一个出点

      以下(x,y,c,d)表示x连向y,流量为c,费用为d

      i表示入点,i+n表示出点

      (st,i,n[i],0) (i+n,ed,n[i],0)相当于先给毛巾,然后再处理费用的问题

      (i+n,i+a+1,inf,fa) (i+n,i+b+1,inf,fb)将出点连向能够A或B消毒后的那一天的入点

      (i,i+1,inf,0)没用完的毛巾可以留到下一天

      (st,i+n,INF,f)可以直接在这一天买毛巾

      这样就能保证每天有n[i]条毛巾用,而且能循环利用了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #define mes(x,y) memset(x,y,sizeof(x))
    #define Maxn 1100
    #define INF 1<<30
    using namespace std;
    struct node{int x,y,c,d,next,other;}a[Maxn*200];int last[Maxn*2],len;
    void ins(int x,int y,int c,int d)
    {
        int k1=++len,k2=++len;
        a[k1].x=x;a[k1].y=y;a[k1].d=d;a[k1].c=c;
        a[k1].next=last[x];last[x]=k1;
        a[k2].x=y;a[k2].y=x;a[k2].d=-d;a[k2].c=0;
        a[k2].next=last[y];last[y]=k2;
        a[k1].other=k2;
        a[k2].other=k1;
    }
    int p[Maxn],st,ed;
    int d[Maxn*2],pos[Maxn*2];
    bool v[Maxn*2];
    queue<int> q;
    bool spfa()
    {
        memset(v,false,sizeof(v));v[st]=true;
        memset(d,63,sizeof(d));d[st]=0;
        q.push(st);
        bool bk=false;
        while(q.empty()==0)
        {
            int x=q.front();q.pop();
            for(int k=last[x];k;k=a[k].next)
            {
                int y=a[k].y;
                if(a[k].c>0&&d[y]>d[x]+a[k].d)
                {
                    d[y]=d[x]+a[k].d;
                    pos[y]=k;
                    if(v[y]==false)
                    {
                        v[y]=true;
                        if(y==ed) bk=true;
                        else q.push(y);
                    }
                }
            }
            v[x]=false;
        }
        return bk;
    }
    int Flow()
    {
        int ans=0;
        while(spfa())
        {
            int mmin=INF;
            for(int x=ed;x!=st;x=a[pos[x]].x)
            {
                mmin=min(a[pos[x]].c,mmin);
            }
            ans+=d[ed]*mmin;
            for(int x=ed;x!=st;x=a[pos[x]].x)
            {
                a[pos[x]].c-=mmin;
                a[a[pos[x]].other].c+=mmin;
            }
        }
        return ans;
    }
    int main()
    {
        int n,A,B,f,fa,fb;
        scanf("%d%d%d%d%d%d",&n,&A,&B,&f,&fa,&fb);
        for(int i=1;i<=n;i++) scanf("%d",&p[i]);
        len=0;mes(last,0);
        st=2*n+1;ed=2*n+2;
        for(int i=1;i<=n;i++) ins(st,i,p[i],0),ins(st,i+n,INF,f);
        for(int i=1;i<=n-A-1;i++) ins(i,i+n+A+1,INF,fa);
        for(int i=1;i<=n-B-1;i++) ins(i,i+n+B+1,INF,fb);
        for(int i=1;i<n;i++) ins(i,i+1,INF,0);
        for(int i=1;i<=n;i++) ins(i+n,ed,p[i],0);
    //  printf("%d
    ",len/2);
    //  for(int i=1;i<=len;i+=2) printf("%d %d %d %d
    ",a[i].x,a[i].y,a[i].c,a[i].d);
        printf("%d
    ",Flow());
        return 0;
    }
    BZOJ1221

    BZOJ2698: 染色

  • 相关阅读:
    net core 使用 rabbitmq
    asp.net core WebApi 返回 HttpResponseMessage
    asp.net core 2.1 WebApi 快速入门
    JQuery EasyUI combobox动态添加option
    php截取字符去掉最后一个字符
    JQuery EasyUI Combobox的onChange事件
    对于不返回任何键列信息的 selectcommand 不支持 updatecommand 的动态 sql 生成
    Access2007 操作或事件已被禁用模式阻止解决办法
    Easyui 中 Tabsr的常用方法
    Win 7 IE11不能下载文件,右键另存为也不行
  • 原文地址:https://www.cnblogs.com/Never-mind/p/10323034.html
Copyright © 2020-2023  润新知