zoukankan      html  css  js  c++  java
  • 一揽子计划

    咕了!

    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: 染色

  • 相关阅读:
    Bootstrap
    格式化字符串
    闭包函数与装饰器
    正则表达式
    jQuery
    分布式-锁-1.1 多线程锁无法满足的场景
    effective python 读书笔记-第22条: 尽量用辅助类来维护程序的状态,而不要用字典
    effective python 读书笔记:第21条-用只能以关键字形式指定的参数来确保代码明晰
    effective python 读书笔记:第20条-用None和文档字符串来描述具有动态默认值的参数
    git如何将上游(upstream)新建分支(origin没有)导入到origin中?
  • 原文地址:https://www.cnblogs.com/Never-mind/p/10323034.html
Copyright © 2011-2022 走看看