APIO 2013

Posted ditoly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了APIO 2013相关的知识,希望对你有一定的参考价值。

这套好丧……跟别的画风好不一样(中国风?)。提答没做也没测,假装只有两题吧。140/200

 

T1.ROBOTS

题目大意:h*w的网格上有n个机器人编号1~n,网格上有空地、墙、顺/逆时针转向器,每次可以把一个机器人朝一个方向推,机器人碰到空地会继续前进,碰到转向器会转向,碰到墙会在前一格停止,同一时间只能有一个机器人在动,编号连续的机器人可以合体,例如2号和3号合成[2,3],[2,3]和[4,6]合成[2,6],问把所有机器人合成一个最少要推几下,无解输出-1。(n<=9,h,w<=500)

思路:先记忆化搜索预处理出每个格子朝四个方向推各会推到哪里(注意可能会无限循环),用f[i][j][k][l]表示[i,j]并成一个机器人位于(k,l)格至少要推几下(内存较紧,需要hash),考虑dijkstra,堆的log难以接受,我想了个方法:答案肯定不会太大,于是每种权值开一个队列,这样复杂度是每次转移O(1),由于还要区间合并,最后时间复杂度大概是O(n^3hw),空间同样。不过实际上这个n^3不是满的,所以理论上应该是很科学的,不过我写了vector,常数好像有点大,本来想手写个链表加上内存回收搞不好就过了,因为懒而且还要打T2暴力就放弃了,最后得分85/100(都是T)。

#include<cstdio>
#include<cstdlib>
#include<vector>
#include<queue>
using namespace std;
#define MN 500
#define MQ 11250000
struct pos{int x,y;}t[MN+5][MN+5][4];
const int o[4][2]={{0,-1},{-1,0},{0,1},{1,0}};
char g[MN+5][MN+5];
int d[MQ+5],p[9][9],pl[45],pr[45],pn,x,y,l,r;
pos work(int x,int y,int f)
{
    if(t[x][y][f].x)return t[x][y][f];
    t[x][y][f]=(pos){-1,-1};
    int r;
    if(g[x][y]==\'.\'||(g[x][y]>=\'1\'&&g[x][y]<=\'9\'))r=f;
    if(g[x][y]==\'A\')r=f?f-1:3;
    if(g[x][y]==\'C\')r=f<3?f+1:0;
    return t[x][y][f]=(g[x][y]&&g[x][y]!=\'x\')?
        work(x+o[r][0],y+o[r][1],r):(pos){x-o[f][0],y-o[f][1]};
}
inline int hash(int x,int y,int l,int r){return x<0?-1:((x-1)*500+y-1)*pn+p[l][r];}
inline void dehash(int z){l=pl[z%pn];r=pr[z%pn];y=(z/=pn)%500+1;x=z/500+1;}
struct MyPQ
{
    vector<queue<int> > v;int x;
    void push(int x,int z)
    {
        if(x<0||(d[x]&&d[x]<=z))return;d[x]=z;
        while(v.size()<z)v.push_back(queue<int>());
        v[z-1].push(x);
    }
    inline int top(){return v[x].front();}
    inline void next(){for(v[x].pop();v[x].empty()&&x<v.size();++x);if(x==v.size())puts("-1"),exit(0);}
    inline void pop(){for(next();x>=d[v[x].front()];next());}
}q;
int main()
{
    int n,w,h,i,j,k,z,zz;
    scanf("%d%d%d",&n,&w,&h);--n;
    for(i=1;i<=h;++i)scanf("%s",g[i]+1);
    for(i=0;i<9;++i)for(j=i;j<9;++j)pl[pn]=i,pr[pn]=j,p[i][j]=pn++;
    for(i=1;i<=h;++i)for(j=1;j<=w;++j)
    {
        for(k=0;k<4;++k)work(i,j,k);
        if(g[i][j]>=\'1\'&&g[i][j]<=\'9\')q.push(hash(i,j,g[i][j]-\'1\',g[i][j]-\'1\'),1);
    }
    while(true)
    {
        dehash(z=q.top());q.pop();
        if(l==0&&r==n)return printf("%d",d[z]-1),0;
        for(i=0;i<l;++i)if(d[zz=hash(x,y,i,l-1)])q.push(hash(x,y,i,r),d[z]+d[zz]-1);
        for(i=8;i>r;--i)if(d[zz=hash(x,y,r+1,i)])q.push(hash(x,y,l,i),d[z]+d[zz]-1);
        for(j=0;j<4;++j)q.push(hash(t[x][y][j].x,t[x][y][j].y,l,r),d[z]+1);
    }
}
View Code

正解:双队列广搜,每种机器人先出算出可以合成它的所有小机器人在各个位置的情况,然后开两个队列,一个是按从小机器人合成的按权值排序的队列,一个是被推着走拓展出的队列,每次取最小的,这种做法理论上和我的是同阶的,不过常数上感觉比我优到不知道哪里去了。

 

T2.TOLL

题目大意:给出一张N点M边的带权联通无向图,每个点上有一些人,给定K条边,要求你定边权后加入原图,使得最后最小生成树上,每个人走到1号点,路径上经过的新加的边的权值和最大。(N<=100,000,M<=300,000,K<=20)

思路:暴力枚举最后保留哪些新加的边,对原图做最小生成树后,每次加入一条非树边,O(n)算出每条边被保留下最大能取的权值,并算出答案,复杂度O(MlogM+NK*2^K),得分55/100。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
inline int read()
{
    int x=0;char c;
    while((c=getchar())<\'0\'||c>\'9\');
    for(;c>=\'0\'&&c<=\'9\';c=getchar())x=(x<<3)+(x<<1)+c-\'0\';
    return x;
}
#define MN 100000
#define MM 300000
#define MK 20
struct Edge{int x,y,w;}E[MM+5],S[MN+5],K[MK+5],T[MN+5];
bool cmp(Edge a,Edge b){return a.w<b.w;}
struct edge{int nx,t,w,u,f;}e[MN*2+50];
int n,k,f[MN+5],h[MN+5],en,u[MN+5],p[MN+5],mx[MK+5];
int fa[MN+5],tf[MN+5],d[MN+5],ps[MN+5];
ll ans,fs[MN+5];
int gf(int k){return f[k]?f[k]=gf(f[k]):k;}
inline void ins(int x,int y,int w,int f=0)
{
    e[++en]=(edge){h[x],y,w,0,f};h[x]=en;
    e[++en]=(edge){h[y],x,w,0,f};h[y]=en;
}
void pre(int x)
{
    for(int i=h[x];i;i=e[i].nx)if(!e[i].u&&e[i].t!=fa[x])
        d[e[i].t]=d[x]+1,fa[e[i].t]=x,tf[e[i].t]=i,pre(e[i].t);
}
void dp(int x,int fa)
{
    ps[x]=p[x];fs[x]=0;
    for(int i=h[x];i;i=e[i].nx)if(!e[i].u&&e[i].t!=fa)
    {
        dp(e[i].t,x);
        ps[x]+=ps[e[i].t];
        fs[x]+=fs[e[i].t];
        if(e[i].f)fs[x]+=(ll)mx[e[i].f]*ps[e[i].t];
    }
}
void work()
{
    int i,j=0,x,y,z,c;
    memset(f,0,sizeof(int)*(n+1));
    for(i=1;i<=k;++i)if(u[i]&&gf(K[i].x)!=gf(K[i].y))
        f[gf(K[i].x)]=gf(K[i].y),T[++j]=K[i];
    memset(h,0,sizeof(int)*(n+1));en=1;
    for(i=1;i<n;++i)ins(S[i].x,S[i].y,S[i].w);
    memset(mx,127,sizeof(mx));
    for(i=1;i<=j;++i)
    {
        pre(1);
        for(x=T[i].x,y=T[i].y,z=-1;x!=y;)
            if(d[x]>d[y]){if(e[tf[x]].w&&e[tf[x]].w>z)z=e[tf[x]].w,c=tf[x];x=fa[x];}
                     else{if(e[tf[y]].w&&e[tf[y]].w>z)z=e[tf[y]].w,c=tf[y];y=fa[y];}
        e[c].u=e[c^1].u=1;mx[i]=z;
        for(x=T[i].x,y=T[i].y;x!=y;)
            if(d[x]>d[y]){if(!e[tf[x]].w)mx[e[tf[x]].f]=min(mx[e[tf[x]].f],z);x=fa[x];}
                     else{if(!e[tf[y]].w)mx[e[tf[y]].f]=min(mx[e[tf[y]].f],z);y=fa[y];}
        ins(T[i].x,T[i].y,0,i);
    }
    dp(1,0);
    if(fs[1]>ans)ans=fs[1];
}
void dfs(int x)
{
    if(x>k){work();return;}
    u[x]=0;dfs(x+1);
    u[x]=1;dfs(x+1);
}
int main()
{
    freopen("toll.in","r",stdin);
    freopen("toll.out","w",stdout);
    int m,i,j;
    n=read();m=read();k=read();
    for(i=0;i<m;++i)E[i].x=read(),E[i].y=read(),E[i].w=read();
    sort(E,E+m,cmp);
    for(i=j=0;i<m;++i)if(gf(E[i].x)!=gf(E[i].y))
        f[gf(E[i].x)]=gf(E[i].y),S[++j]=E[i];
    for(i=1;i<=k;++i)K[i].x=read(),K[i].y=read();
    for(i=1;i<=n;++i)p[i]=read();
    dfs(1);
    cout<<ans;
    fclose(stdin);fclose(stdout);return 0;
}
View Code

正解:一开始先把K条边边权设为0和M条边放一起跑最小生成树,K条边把最小生成树分成K+1块,每一块最后无论如何一定是连在一起的,可以缩成一个点,然后再按上面的暴力做,复杂度O(MlogM+K^2*2^K)。

 

以上是关于APIO 2013的主要内容,如果未能解决你的问题,请参考以下文章

[APIO2013]道路费用

UOJ #109「APIO2013」TASKSAUTHOR

bzoj3206 [Apio2013]道路费用

APIO2013道路费用(TOLL)

[APIO2013]机器人

[APIO2013]机器人