NOI 2008 Day2 ~ 2009

[NOI2008]糖果雨

啊啊啊先挖坑,推荐

http://blog.sina.com.cn/s/blog_86942b1401015yln.html

CODE:

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2003;
const int maxm=4003;
const int maxc=1e6+10;
int q,len,n,m;
int s[2][maxn][maxm],x[maxc],y[maxc];

inline int lowbit(int x){return x&-x;}
inline void add(int p,int x,int y,int v)
{
    x++,y++;
    for(;x<maxn;x+=lowbit(x))
        for(int i=y;i<maxm;i+=lowbit(i)) s[p][x][i]+=v;
}

inline int sum(int p,int x,int y)
{
    if(x<0 || y<0) return 0;
    int k=0;
    if(++x>n) x=n+1; if(++y>m) y=m+1;
    for(;x;x-=lowbit(x))
        for(int i=y;i;i-=lowbit(i)) k+=s[p][x][i];
    return k;
}

inline void update(int t,int c,int l,int r,int d)
{
    x[c]=(t-l*d+n)%n,y[c]=r-l;
    add(0,x[c],y[c]+x[c],1),add(1,x[c],y[c]-x[c]+n,1);
}

inline void del(int c)
{
    add(0,x[c],y[c]+x[c],-1),add(1,x[c],y[c]-x[c]+n,-1);
}

inline int area(int p,int x,int y,int xx,int yy)
{
    return sum(p,xx,yy)+sum(p,x-1,y-1)-sum(p,x-1,yy)-sum(p,xx,y-1);
}

inline int ask(int t,int l,int r)
{
    int d=r==len;
    return area(0,t,t+l,t+r,m)+area(0,0,l+t-n,t+r-n-d,m)+area(1,n-r+t+d,l-t,n,m)+area(1,t-r,l-t+n,t-1,m);
}

int main()
{
    scanf("%d%d",&q,&len);
    n=len<<1,m=len<<2;
    while(q--)
    {
        int t,c,l,r,d,p;
        scanf("%d%d",&p,&t);
        if(p==1)
        {
            scanf("%d%d%d%d",&c,&l,&r,&d);
            update(t,c,l,r,d);
        }
        else if(p==2)
        {
            scanf("%d%d",&l,&r);
            printf("%d\n",ask(t%n,l,r));
        }
        else
        {
            scanf("%d",&c);
            del(c);
        }
    }
    return 0;
}
View Code

 

 

[NOI2009]诗人小G

注意字母和空格也算‘字符’。

f[i]表示考虑到i这一句诗的最少花费。

我们显然有经典的1D1D模型:f[i]=min(f[j]+cost[j+1][i]);

然而n^2只有30(或者20)

想办法啊,观察cost,'发现'满足四边形不等式(可以直接打表观察决策单调的规律),那么f[i]就具有决策单调性,每个f[j]可以更新的f[i]是一个区间,而且所有的f[j]的区间不重不漏,发现我们总能找到一个i,使得i之前的f[i]不需要f[j]达到最优,而之后的总可以借助f[j]达到最优,那么用一个队列存所有的线段以及用谁更新,每次先弹掉右端小于当前i的区间(其实每次只弹一个),然后用队首更新f[i],入队的时候尝试通过二分把队尾的区间右端往回压(当队尾的左端点都是i更新更优的时候,直接弹掉q[top]),即找到i和q[top]的更新分界点,入队就好了。nlogn的吧。

CODE:

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
struct point
{
    int l,r,p;
}q[maxn*20];
int n,T;
long double L,P,sum[maxn],f[maxn];
int ans[maxn],nans,from[maxn];
char ss[maxn][55];

inline long double Pow(long double x,long long b)
{
    if(x<0)x=-x;
    long double res=1;
    for(;b;b>>=1)
    {
        if(b&1) res=res*x;
        x=x*x;
    }
    return res;
}

inline long double cal(int j,int i)
{
    return f[j]+Pow(sum[i]-sum[j]+i-j-1-L,P);
}

int find(point t,int x)
{
    int l=t.l-1,r=t.r+1;
    while(l+1<r)
    {
        int mid=(l+r)>>1;
        if(cal(t.p,mid)<cal(x,mid))
            l=mid;
        else r=mid;
    }
    return r;
}

inline void dp()
{
    int head=1,tail=0;
    q[++tail]=(point){0,n,0};
    for(int i=1;i<=n;i++)
    {
        if(head<=tail && q[head].r<i) head++;
        f[i]=cal(q[head].p,i);
        from[i]=q[head].p;
        if(head>tail || cal(i,n)<=cal(q[tail].p,n))
        {
            while(head<=tail && cal(i,q[tail].l)<=cal(q[tail].p,q[tail].l)) tail--;
            if(head>tail) q[++tail]=(point){i,n,i};
            else
            {
                int pos=find(q[tail],i);
                q[tail].r=pos-1;
                q[++tail]=(point){pos,n,i};
            }
        }
    }
}

inline void print()
{
    int tmp=n;
    nans=0;
    while(1)
    {
        ans[++nans]=tmp;
        if(tmp==0) break;
        tmp=from[tmp];
    }
    for(int i=nans;i>1;i--)
    {
        int be=ans[i]+1;
        for(int j=be;j<=ans[i-1];j++)
            printf("%s%c",ss[j]+1,j==ans[i-1]?'\n':' ');
    }
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(f,0,sizeof(f));
        scanf("%d%Lf%Lf",&n,&L,&P);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",ss[i]+1);
            sum[i]=sum[i-1]+strlen(ss[i]+1);
        }
        dp();
        if(f[n]>1000000000000000000LL)
        {
            printf("Too hard to arrange\n");
            printf("--------------------");
            if(T) printf("\n");
            continue;
        }
        printf("%.0Lf\n",f[n]);
        print();
        printf("--------------------");
        if(T) printf("\n");
    }
    return 0;
}
View Code

 

[NOI2009]植物大战僵尸

啊。。很棘手的一道题。

一眼最大权闭合子图,可是有毛病的啊,图中会有环。

想想就会发现,对于环来说,我们肯定无论如何也得不到环中的分,所以环中的点所保护的点当然也无法得分,所以要通过拓扑来忽略这些点,删掉环和从环伸出去的点?反向拓扑。

然后就很傻了,模板。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=610*610*6+10;
const int inf=1e9;
struct point
{
    int to;
    int nxt;
    int c;
}edge1[maxn],edge[maxn];
int n,m,cnt=0,s,t,sum,tot1,tot;
int level[maxn],vis[maxn],val[maxn];
int head1[maxn],head[maxn],in[maxn];
int map[50][50];

inline void add1(int u,int v)
{
    tot1++;
    edge1[tot1].to=v;
    edge1[tot1].nxt=head1[u];
    head1[u]=tot1;
}

inline void add(int u,int v,int c)
{
    edge[tot].to=v;
    edge[tot].nxt=head[u];
    edge[tot].c=c;
    head[u]=tot;
    tot++;
    
    edge[tot].to=u;
    edge[tot].nxt=head[v];
    edge[tot].c=0;
    head[v]=tot;
    tot++;    
}

inline void Topo()
{
    queue<int> q;
    for(int i=1;i<=n*m;++i)
        if(in[i]==0)
        {
            q.push(i);
            vis[i]=1;    
        }
    while(!q.empty())
    {
        int tt=q.front();
        q.pop();
        for(int i=head1[tt];i;i=edge1[i].nxt)
        {
            int v=edge1[i].to;
            in[v]--;
            if(in[v]==0)
                q.push(v);    
        }    
    }
}

inline bool Bfs()
{
    queue<int> q;
    memset(level,-1,sizeof(level));
    level[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int tt=q.front();
        q.pop();
        for(int i=head[tt];~i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(level[v]==-1 && edge[i].c)
            {
                level[v]=level[tt]+1;
                q.push(v);    
            }
        }
    }    
    if(level[t]==-1) return false;
    return true;
}

int dfs(int x,int low)
{
    if(x==t || low==0)
        return low;
    int res=0;
    for(int i=head[x];~i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(level[v]==level[x]+1 && edge[i].c)
        {
            int ww=dfs(v,min(low,edge[i].c));
            low-=ww;
            res+=ww;
            edge[i].c-=ww;
            edge[i^1].c+=ww;
            if(low==0) break;
        }
    }    
    if(res!=low || res==0)
        level[x]=-1;
    return res;
}

int Dinic()
{
    int res=0;
    while(Bfs())
    {
        res+=dfs(s,inf);
    }
    return res;
}

int main()
{
    scanf("%d%d",&n,&m);
    s=0,t=m*n+1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            map[i][j]=++cnt;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            int vv,nn;
            scanf("%d%d",&vv,&nn);
            val[map[i][j]]=vv;
            for(int t=1;t<=nn;t++)
            {
                int xx,yy;
                scanf("%d%d",&xx,&yy);
                xx++,yy++;
                add1(map[i][j],map[xx][yy]);
                in[map[xx][yy]]++;
            }
            if(j!=m)
            {
                add1(map[i][j+1],map[i][j]);
                in[map[i][j]]++;    
            }
        }
    Topo();
    memset(head,-1,sizeof(head));
    for(int x=1;x<=m*n;++x)
    if(!in[x])
    {
        if(val[x]>0)
        {
            add(s,x,val[x]);
            sum+=val[x];    
        }
        if(val[x]<0)
            add(x,t,-val[x]);
        for(int i=head1[x];i;i=edge1[i].nxt)
        {
            int v=edge1[i].to;
            if(!in[x])
                add(v,x,inf);    
        }    
    }
    int Mincut=Dinic();
    printf("%d",sum-Mincut);
    return 0;
}
View Code

 

[NOI2009]管道取珠

很可爱的一道题/

只要发现根据乘法原理,所谓Ai^2就是再搬一套双筒管放在下面,使得上下系统同时达到同种状态的操作方案数,那就很naive了。

用f[i][s1][s2]表示操作i步,上面的双筒的上管已经弹出s1个球,下面的双筒的上管已经弹出s2个球的方案数。第一维可以压掉。

O((n+m)n^2)的,卡着过。

CODE:

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=505;
const int mod=1024523;
int n,m;
long long f[2][maxn][maxn];
char a[maxn],b[maxn],c[maxn];

int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s",a+1);
    scanf("%s",b+1);

        
    f[0][0][0]=1;
    int cur=1;
    for(int t=1;t<=n+m;t++)
    {
        memset(f[cur],0,sizeof(f[cur]));
        for(int i=max(0,t-m);i<=min(n,t);i++)
            for(int j=max(0,t-m);j<=min(n,t);j++)
            {
                if(t-i && t-j && b[t-i]==b[t-j])
                    f[cur][i][j]=(f[cur][i][j]+f[cur^1][i][j])%mod;
                if(i && j && a[i]==a[j])
                    f[cur][i][j]=(f[cur][i][j]+f[cur^1][i-1][j-1])%mod;
                if(t-j && i && a[i]==b[t-j])
                    f[cur][i][j]=(f[cur][i][j]+f[cur^1][i-1][j])%mod;
                if(t-i && j && b[t-i]==a[j])
                    f[cur][i][j]=(f[cur][i][j]+f[cur^1][i][j-1])%mod;
            }    
        cur^=1;
    }
    printf("%d",f[cur^1][n][n]);
    return 0;
}
View Code

 

posted @ 2018-05-28 10:44  Captain_fcj  阅读(176)  评论(1编辑  收藏  举报