2013

来源:互联网 发布:乐乎青年公寓朝阳 编辑:程序博客网 时间:2024/04/30 06:04

2013D2T1
积木赛
Task
n个初始为0的数,求最少的操作次数变成目标数组。
一次操作:选择区间[ l , r ]内所有数+1
n<=1e5,0<=hi<=1e4

Solution
贪心
为使操作次数最少,每次选的区间应尽可能的大。因此每次贪心选一段最长都是正数的区间,区间内数-1.最值可以用堆维护。
如果当前选出的区间操作完后不含0,那么下一次还是会选择这个区间,为减少操作次数,
如果一次性减去这个区间的最小值,就一定会含0,区间分成2个子区间。

用堆写还是太麻烦了,不如dfs,每次找到这个区间内的最小值,分成2个子区间。

const int M=1e5+5;int A[M],n;struct node{    int l,r,x;    bool operator<(const node &t)const{        r-l>t.r-t.l;    }};priority_queue<node>Q;inline void input(){    int i;    rd(n);    rep(i,1,n)rd(A[i]);}inline void add(int L,int R,int x){    int i,k,mi,l,r;    rep(i,L,R)A[i]-=x;    rep(i,L,R){        if(A[i-1]==0&&A[i]>0){            l=i;            mi=A[i];        }        MIN(mi,A[i]);        if(A[i]>0&&A[i+1]==0){            Q.push((node){l,i,mi});        }    }}inline void solve(){    int i,j,k,ans=0,l,r;    node res;    while(!Q.empty()){        res=Q.top();        Q.pop();        ans+=res.x;        add(res.l,res.r,res.x);    }    printf("%d\n",ans);}int main(){    input();    add(1,n,0);    solve();    return 0;}

LIN452大神提供了一种O(n)的方法。
刷漆
区间加除了线段树以外,还可以用刷漆法处理,左端点+1,右端点+1的位置-1。
对于i点而言,权值从0变成hi,说明[1,i]操作前缀和是hi。对于i-1点而言,操作的前缀和是h[i-1],说明对i点的操作数是h[i]-h[i-1],是以i为操作区间左端点的最少次数。

const int M=1e5+5;int A[M];int n;int main(){    int i,j,k,ans=0;    rd(n);    rep(i,1,n)rd(A[i]);    rep(i,1,n)ans+=max(0,A[i]-A[i-1]);    printf("%d\n",ans);}

2013D2T2
花匠
Task
在n个数去除一些数,使剩余的数满足条件A或条件B,求最多剩余数?
条件A:对所有的i,g[2i]>g[2i-1],g[2i]>g[2i+1]
条件B:对所有的i,g[2i]小于g[2i-1],g[2i]小于g[2i+1]
n<=1e5,hi<=1e6

Solution
考察LIS的dp思想。

发现最后满足的数是波浪锯齿状,且2个为1组。每个数的选择:不选,组内第一个数,组内第二个数。每一个数都是接在符合条件的另一个数后面。
如果定义dp[0][i],i作为组第一个,dp[1][i]i作为组第二个。
条件A:
Dp[0][i]=max(dp[1][j])+1 j小于i&&A[j]>A[i]
Dp[1][i]=max(dp[0][i])+1 j小于i&&A[j]小于A[i]
同样的条件B的式子也可以列出。
普通的转移是O(n^2)

但是我们可以借助线段树优化,因为是维护前缀后缀的最值,也可以用树状数组来代替,代码更短,效率更高。
但是比赛时以为后缀的下标搞错了,一个测试点WA了。
X对应的后缀下标是tot-x+1.因为[1,tot] 对应[tot,1]

const int M=1e5+5;int A[M],B[M];int n,ans,tot;struct P70{    int dp[2][1005];    inline void solve(){        int i,j,k,ans=0;        rep(i,1,n)rd(A[i]);        A[0]=1e9;        rep(i,1,n){//条件A             rep(j,0,i-1){                if(A[j]>A[i])MAX(dp[0][i],dp[1][j]+1);                if(A[j]<A[i])MAX(dp[1][i],dp[0][j]);            }        }        rep(i,1,n){            MAX(ans,dp[0][i]*2-1);            MAX(ans,dp[1][i]*2);        }        memset(dp,0,sizeof(dp));        A[0]=-1;        rep(i,1,n){            rep(j,0,i-1){                if(A[j]<A[i])MAX(dp[0][i],dp[1][j]+1);                if(A[j]>A[i])MAX(dp[1][i],dp[0][j]);            }        }        rep(i,1,n){            MAX(ans,dp[0][i]*2-1);            MAX(ans,dp[1][i]*2);        }        printf("%d\n",ans);    }}P70;struct Tree{    int bit[M];    inline void clear(){memset(bit,0,sizeof(bit));}    inline void modify(int i,int x){        while(i<=tot){            MAX(bit[i],x);            i+=lowbit(i);        }    }    inline int query(int i){        int mx=0;        while(i>0){            MAX(mx,bit[i]);            i-=lowbit(i);        }        return mx;    }}T[2];struct P100{    inline void init(){        int i,j,k;        rep(i,1,n)rd(A[i]),B[i-1]=A[i];        sort(B,B+n);        tot=unique(B,B+n)-B;        rep(i,1,n)//离散             A[i]=lower_bound(B,B+tot,A[i])-B+1;    }    inline void solve(){        int i,j,k,ans=0,a,b;        init();        T[0].clear();T[1].clear();        rep(i,1,n){            a=T[1].query(tot-A[i])+1;            b=T[0].query(A[i]-1)+1;            T[0].modify(A[i],a);            T[1].modify(tot-A[i]+1,b);            MAX(ans,max(a,b));        }        T[0].clear();T[1].clear();        rep(i,1,n){            a=T[1].query(A[i]-1)+1;            b=T[0].query(tot-A[i])+1;            T[0].modify(tot-A[i]+1,a);            T[1].modify(A[i],b);            MAX(ans,max(a,b));        }        printf("%d\n",ans);    }}P100;int main(){    rd(n);    if(n<=1000){P70.solve();return 0;}    P100.solve();    return 0;}

2013D2T3
华容道
Task
N*m棋盘上有n*m-1个棋子,1个空白格,棋子有若干个不能移动,剩余的可以移动,空白格只能和四周可以移动的格子交换位置。
Q个询问,除了不能移动的棋子外,空白格,其余棋子,初始目标位置都可能改变,求某棋子从初始位置到目标位置的最小步数。
60%数据,n,m<=30,q<=10
100% 数据,n,m<=30,q<=500

Solution
60分bfs
类似judge1205推箱子,从初始位置移动到目标位置的最小步数,唯一不同的是该题是多次询问。bfs可以求解最小步数的问题。
因为我们只关心空白格子和初始特别格子的位置,因此在bfs时只用记录这2个的坐标。
搜索时,注意判重,不合法的情况。

值得一提的是,Y同学用dfs?bfs?做没有判重,电脑死机了。唉╮(╯▽╰)╭

100分 最短路
题目中有一句话:要将指定块移入目标位置,必须先将空白块移入目标位置。给我带来了很大的提示。
更广泛的说,如果x要达到某个位置,必须先将空白格移到该位置,再交换,且该位置一定在x的四周。
60分是搜索空白格的路径,但100分是搜索x的路径,相比60分而言,同样是bfs,但是主体不同。可以一步代替多步,从无方向到有方向。
如果我已知x位置和空白格位置(一定在x的四周)那么x只能和空白格交换,因此可以得知x的新位置,空白格也必须移动到x的四周。
因此可以预处理点(x,y)在不经过中心点的情况下,到达中心点四周的最短路。

const int M=30;int rx[]={0,1,0,-1},ry[]={1,0,-1,0};int dis[M][M][4][M][M],val[M][M][4];bool A[M][M],vis[M][M],use[M][M][4];int n,m,q;struct qu{    int x,y;}Qu[905];struct node{    int x,y,d,v;    bool operator<(const node &t)const{        return v>t.v;    }};priority_queue<node>Q;inline void input(){    int i,j,k;    rd(n);rd(m);rd(q);    rep(i,0,n-1)        rep(j,0,m-1)rd(A[i][j]);}inline bool check(int x,int y){    return x>=0&&x<n&&y>=0&&y<m&&A[x][y]==1;}inline void bfs(int a,int b,int p){    int i,j,k,x,y,l,r,nx,ny;    qu res;    x=a+rx[p],y=b+ry[p];    memset(vis,0,sizeof(vis));    vis[a][b]=vis[x][y]=1;    l=r=dis[a][b][p][x][y]=0;    Qu[r++]=(qu){x,y};    while(l<r){        res=Qu[l++];        rep(i,0,3){            nx=res.x+rx[i],ny=res.y+ry[i];            if(!check(nx,ny)||vis[nx][ny])continue;            vis[nx][ny]=1;            dis[a][b][p][nx][ny]=dis[a][b][p][res.x][res.y]+1;            Qu[r++]=(qu){nx,ny};        }    }}inline void init(){    int i,j,k,ii,jj;    memset(dis,-1,sizeof(dis));    rep(i,0,n-1)        rep(j,0,m-1){            if(A[i][j]==0)continue;            rep(k,0,3)                if(check(i+rx[k],j+ry[k])){                    bfs(i,j,k);                }        }}inline int dijkstra(int tx,int ty){    int i,j,k,x,y;    node res;    while(!Q.empty()){        res=Q.top();Q.pop();        if(use[res.x][res.y][res.d])continue;        if(res.x==tx&&res.y==ty)return val[res.x][res.y][res.d];        use[res.x][res.y][res.d]=1;        x=res.x+rx[res.d],y=res.y+ry[res.d];        rep(i,0,3){            k=dis[x][y][i][res.x][res.y];            if(k>=0){                if(res.v+k+1<val[x][y][i])val[x][y][i]=res.v+k+1;                Q.push((node){x,y,i,res.v+k+1});            }        }    }    return -1;}inline void solve(){    int i,j,k,wx,wy,bx,by,tx,ty;    while(q--){        rd(wx);rd(wy);rd(bx);rd(by);rd(tx);rd(ty);        wx--,wy--,bx--,by--,tx--,ty--;        if(bx==tx&&by==ty){puts("0");continue;}        memset(use,0,sizeof(use));        memset(val,0x3f,sizeof(val));        while(!Q.empty())Q.pop();        rep(i,0,3){            k=dis[bx][by][i][wx][wy];            if(~k){                val[bx][by][i]=k;                Q.push((node){bx,by,i,k});            }        }        printf("%d\n",dijkstra(tx,ty));    }}int main(){    input();    init();    solve();    return 0;}
1 0
原创粉丝点击