一揽子计划

咕了!

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\n",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\n",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\n",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\n",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\n",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\n",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\n");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\n",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\n",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\n",l3,dis(x,l3)+dis(y,l3)+dis(z,l3));
        else if(l1==l3) printf("%d %d\n",l2,dis(x,l2)+dis(y,l2)+dis(z,l2));
        else if(l2==l3) printf("%d %d\n",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\n");
        else printf("true\n");
    }
    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\n",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\n",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\n",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("\n");
//    for(int i=1;i<=cnt+1;i++) printf("%.1lf ",f2[to[i]]);printf("\n");
//    printf("L\n");
    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\n",i,L[i],l[i],ml[i]);
    }
    R[cnt+1]=r[cnt+1]=mx=0;sum=0;
//    printf("R\n");
    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\n",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\n",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\n",tmp[i].tp,tmp[i].x,tmp[i].y,tmp[i].nn);printf("\n");
    for(int i=1;i<=cnt;i++) printf("%d\n",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\n",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\n",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\n",pre[sp]);
            }
        }
        else
        {
            printf("%d\n",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\n",sit);
//        if(tr[1].c<0)
//        {
//            printf("WA\n");
//            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\n");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\n",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\n",len/2);
//  for(int i=1;i<=len;i+=2) printf("%d %d %d %d\n",a[i].x,a[i].y,a[i].c,a[i].d);
    printf("%d\n",Flow());
    return 0;
}
BZOJ1221

BZOJ2698: 染色

posted @ 2019-01-26 11:49  Star_Feel  阅读(668)  评论(0编辑  收藏  举报