2017-4-14校内训练

hzwer拿了几道noi中比较简单的给我们做

A.[NOI2009]植物大战僵尸

思路:考虑最小割,如果一个植物的权值x是正的,我们先默认吃掉它,使答案加上x并让S向这个点连x,割这条边相当于不吃这个植物,否则让这个点向T连-x,割这条边相当于吃这个植物,每个植物让它能攻击到的格子和它的前一格向它连INF,表示要么不吃它能保护到的,要么吃它,如果构成了环,这些植物必然都不能被吃,判完环后环上的点全部向T连INF即可。

#include<cstdio>
#include<cstring>
inline int read()
{
    int x,f=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=0;
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0';
    return f?x:-x;
}
#define MV 600
#define ME 400000
#define S MV+1
#define T MV+2
#define INF 0x7FFFFFFF
#define p(x,y) ((x)*30+(y)+1)
struct edge{int nx,t,w;}e[ME*2+5];
int h[MV+5],en=1,d[MV+5],c[MV+5],q[MV+5],qn,df[MV+5],lw[MV+5],cnt,inq[MV+5];
inline void ins(int x,int y,int w)
{
    e[++en]=(edge){h[x],y,w};h[x]=en;
    e[++en]=(edge){h[y],x,0};h[y]=en;
}
bool bfs()
{
    int i,j;
    memset(d,0,sizeof(d));
    for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=c[q[i]]=h[q[i]];j;j=e[j].nx)
        if(e[j].w&&!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1;
    return d[T];
}
int dfs(int x,int r)
{
    if(x==T)return r;
    int k,u=0;
    for(int&i=c[x];i;i=e[i].nx)if(e[i].w&&d[e[i].t]==d[x]+1)
    {
        k=dfs(e[i].t,r-u<e[i].w?r-u:e[i].w);
        e[i].w-=k;e[i^1].w+=k;u+=k;
        if(u==r)return r;
    }
    return d[x]=0,u;
}
void dfs(int x)
{
    int i,u;
    df[x]=lw[x]=++cnt;inq[q[qn++]=x]=1;
    for(int i=h[x];i;i=e[i].nx)if(e[i].w)
    {
        if(!df[e[i].t])dfs(e[i].t);
        if(inq[e[i].t]&&lw[e[i].t]<lw[x])lw[x]=lw[e[i].t];
    }
    u=q[qn-1]!=x;
    if(df[x]==lw[x])for(;q[qn]!=x;u?(ins(q[qn],T,INF),0):0)inq[q[--qn]]=0;
}
int main()
{
    int n,m,i,j,k,x,ans=0;
    n=read();m=read();
    for(i=0;i<n;++i)for(j=0;j<m;++j)
    {
        x=read();
        if(x>0)ans+=x,ins(S,p(i,j),x);
        else ins(p(i,j),T,-x);
        if(j)ins(p(i,j-1),p(i,j),INF);
        for(k=read();k--;)x=read(),ins(p(x,read()),p(i,j),INF);
    }
    for(i=0;i<n;++i)for(j=0;j<m;++j)if(!df[p(i,j)])dfs(p(i,j));
    while(bfs())ans-=dfs(S,INF);
    printf("%d",ans);
}

B.[Noi2011]阿狸的打字机

之前做过了,题解戳这里

C.[NOI2005]瑰丽华尔兹

思路:用f[i][j][k]表示前i段时间过后,钢琴在(j,k)的最长路程,转移很显然单调队列嘛,复杂度O(nmk)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0';
    return x;
}
#define MN 200
int f[MN+5][MN+5][MN+5],q[MN+5],ql,qr;
char s[MN+5][MN+5];
int main()
{
    int n,m,x,y,t,i,j,xx,yy,ans=0;
    n=read();m=read();
    memset(f,200,sizeof(f));
    x=read();f[0][x][read()]=0;t=read();
    for(i=1;i<=n;++i)scanf("%s",s[i]+1);
    for(i=1;i<=t;++i)
    {
        x=read();x=read()-x+1;y=read();
        if(y==1)for(yy=1;yy<=m;++yy)for(ql=1,qr=0,xx=n;xx;--xx)
        {
            while(ql<=qr&&f[i-1][xx][yy]+xx>=f[i-1][q[qr]][yy]+q[qr])--qr;q[++qr]=xx;
            while(q[ql]-xx>x)++ql;
            if(s[xx][yy]=='.')f[i][xx][yy]=f[i-1][q[ql]][yy]+q[ql]-xx;
            else ql=1,qr=0;
        }
        if(y==2)for(yy=1;yy<=m;++yy)for(ql=1,qr=0,xx=1;xx<=n;++xx)
        {
            while(ql<=qr&&f[i-1][xx][yy]-xx>=f[i-1][q[qr]][yy]-q[qr])--qr;q[++qr]=xx;
            while(xx-q[ql]>x)++ql;
            if(s[xx][yy]=='.')f[i][xx][yy]=f[i-1][q[ql]][yy]+xx-q[ql];
            else ql=1,qr=0;
        }
        if(y==3)for(xx=1;xx<=n;++xx)for(ql=1,qr=0,yy=m;yy;--yy)
        {
            while(ql<=qr&&f[i-1][xx][yy]+yy>=f[i-1][xx][q[qr]]+q[qr])--qr;q[++qr]=yy;
            while(q[ql]-yy>x)++ql;
            if(s[xx][yy]=='.')f[i][xx][yy]=f[i-1][xx][q[ql]]+q[ql]-yy;
            else ql=1,qr=0;
        }
        if(y==4)for(xx=1;xx<=n;++xx)for(ql=1,qr=0,yy=1;yy<=m;++yy)
        {
            while(ql<=qr&&f[i-1][xx][yy]-yy>=f[i-1][xx][q[qr]]-q[qr])--qr;q[++qr]=yy;
            while(yy-q[ql]>x)++ql;
            if(s[xx][yy]=='.')f[i][xx][yy]=f[i-1][xx][q[ql]]+yy-q[ql];
            else ql=1,qr=0;
        }
    }
    for(i=1;i<=n;++i)for(j=1;j<=m;++j)ans=max(ans,f[t][i][j]);
    printf("%d",ans);
}

D.[Noi2014]动物园

思路:首先kmp弄出next数组,num[i]就是每次令i=next[i],在变成0前有多少次不超过i/2,先不考虑不超过i/2的限制,则num[i]=num[next[i]]+1,对于每个next[i]>i/2,扣掉它next几次后会小等于i/2,由于它的循环节是i-next[i],每次取next都会使它的值减小i-next[i],除一下就好了,复杂度O(n)。

#include<cstdio>
#include<cstring>
#define MN 1000000
#define MOD 1000000007
char s[MN+5];
int nx[MN+5],f[MN+5];
int main()
{
    int n,i,j,ans;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s",s+1);
        for(f[1]=1,i=2,j=0;s[i];++i)
        {
            while(j&&s[i]!=s[j+1])j=nx[j];
            if(s[i]==s[j+1])++j;
            f[i]=f[nx[i]=j]+1;
        }
        for(ans=i=1;s[i];++i)
        {
            if(nx[i]*2>i)f[i]-=(nx[i]-i/2+i-nx[i]-1)/(i-nx[i]);
            ans=1LL*ans*f[i]%MOD;
        }
        printf("%d
",ans);
    }
}