NOIP2012开车旅行&疫情控制解题记录
注:本文转自我在csdn的博客:http://blog.csdn.net/six_people/article/details/78004729
首先是开车旅行
题目地址:
http://fzoj.xndxfz.com/JudgeOnline/problem.php?id=1349
题目大意:
这个我就不说了,看不懂的多读几遍题。(PS:其实我题意读错了3次,捂脸)
题解:
因为A和B是一人开一天,所以我们就可以将A与B各开一天看作一次开车。这样的话只要确定了起始位置,我们就可以通过倍增的方法快速找到总距离不超过X的所能到达的最远的点了。另外多说一句,这里如果是直接用n的时间查找最远点应该会超时,而如果用倍增可以将这里的n变为logn。
个人认为这道题比较坑的是预处理。也就是找距离当前城市第一近和第二近的城市。这个最开始我是想直接把全部点快排一遍,然后再找最近和第二近的城市,然后。。。我发现这个很难写,放弃了。这个时候,万能的Apache553给我介绍了一下set。我又看了一下Y_sofun的代码才A掉这道题。
下面就贴代码吧。
#include<set> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define N 100005 using namespace std; struct node { int ord,high; friend bool operator<(node x,node y) { return x.high<y.high; } }h[N]; struct node2 { int ord,val,high; friend bool operator<(node2 x,node2 y) { if(x.val==y.val) { return x.high<y.high; } return x.val<y.val; } node2 () {} node2(int a,int b,int c) : val(a),ord(b),high(c) {} }t[10]; int n,fa[3][N],d[3][N],xaq,xbq,m,x0; int grand[N][20],disa[N][20],disb[N][20]; set<node> S; set<node>::iterator iter; inline int read() { int out=0,flagg=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') flagg=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { out=out*10+ch-'0'; ch=getchar(); } return out*flagg; } void Init() { memset(grand,0,sizeof(grand)); memset(h,0,sizeof(0)); memset(fa,0,sizeof(fa)); // scanf("%d",&n); n=read(); int cnt=0,i,j; for(i=1;i<=n;i++) { // scanf("%d",&h[i].high); h[i].high=read(); h[i].ord=i; } for(i=n;i>=1;i--) { cnt=0; S.insert(h[i]); iter=S.find(h[i]); if(iter!=S.begin()) { --iter; t[++cnt]=node2(abs((*iter).high-h[i].high),(*iter).ord,(*iter).high); if(iter!=S.begin()) { --iter; t[++cnt]=node2(abs((*iter).high-h[i].high),(*iter).ord,(*iter).high); ++iter; } ++iter; } if((++iter)!=S.end()) { t[++cnt]=node2(abs((*iter).high-h[i].high),(*iter).ord,(*iter).high); if((++iter)!=S.end()) { t[++cnt]=node2(abs((*iter).high-h[i].high),(*iter).ord,(*iter).high); --iter; } --iter; } sort(t+1,t+cnt+1); if(t[1].ord) { fa[1][i]=t[1].ord; d[1][i]=t[1].val; } if(t[2].ord) { fa[2][i]=t[2].ord; d[2][i]=t[2].val; } /* for(int j=1;j<=cnt;j++) { printf("%d ",t[j].ord); } printf("\n"); */ } for(i=1;i<=n;i++) { int f1=fa[2][i],f2=fa[1][f1]; // printf("\n%d %d\n",f1,f2); grand[i][0]=f2; if(f1) { disa[i][0]=d[2][i]; } else { disa[i][0]=0; } if(f2) { disb[i][0]=d[1][f1]; } else { disb[i][0]=0; } } for(j=1;j<=18;j++) { for(i=1;i<=n;i++) { if(grand[grand[i][j-1]][j-1]) { grand[i][j]=grand[grand[i][j-1]][j-1]; disa[i][j]=disa[grand[i][j-1]][j-1]+disa[i][j-1]; disb[i][j]=disb[grand[i][j-1]][j-1]+disb[i][j-1]; } } } } double work1(int k,int x) { int i,xa=0,xb=0; for(i=18;i>=0;i--) { if(grand[k][i]) { if(disa[k][i]+disb[k][i]<=x) { x-=(disa[k][i]+disb[k][i]); xa+=disa[k][i]; xb+=disb[k][i]; k=grand[k][i]; } } } if(fa[2][k]&&d[2][k]<=x) { xa+=d[2][k]; } // printf("%d %d\n",xa,xb); if(xb==0) return 1.0f/0.0f; return (double)xa/(double)xb; } void work2(int k,int x) { int i; xaq=0,xbq=0; for(i=18;i>=0;i--) { if(grand[k][i]&&disa[k][i]+disb[k][i]<=x) { x-=(disa[k][i]+disb[k][i]); xaq+=disa[k][i]; xbq+=disb[k][i]; k=grand[k][i]; } } if(fa[2][k]&&d[2][k]<=x) { xaq+=d[2][k]; } } int main() { // freopen("car.out","w",stdout); Init(); int i,y,x; double ans=1.0f/0.0f,temp; int ans_1; scanf("%d",&x0); for(i=1;i<=n;i++) { // printf("%d\n",work1(i,x0)); // ans=min(ans,work1(i,x0)); temp=work1(i,x0); if(temp<ans) { ans=temp; ans_1=i; } } printf("%d\n",ans_1); // scanf("%d",&m); m=read(); for(i=1;i<=m;i++) { // scanf("%d%d",&y,&x); y=read(); x=read(); work2(y,x); printf("%d %d\n",xaq,xbq); } /* for(i=1;i<=n;i++) { printf("%d\n",grand[i][1]); } */ return 0; }
下面就来说说疫情控制
题目地址:
http://fzoj.xndxfz.com/JudgeOnline/problem.php?id=1352
题目大意:
一个地图是一棵树的国家的首都(也就是树根)爆发的疫情,要派遣军队去镇守其中的一些城市以防止疫情传播到边境城市,军队花费的时间为其走过路径的长度。求能控制的最少花费的时间。
题解:
因为才做过开车旅行,所以我很快就想到了倍增,然后这是道求最少时间的题,我又想到了二分答案,于是初步的解法就是倍增+二分答案。
首先,我们要让每支军队尽量向首都走,这样才能发挥出他的最大作用,所以我们就可以在用倍增的方法求出在规定的时间内每只军队能否到达首都,如果能,记录下他到达首都后剩余的时间,如果不能,记录下他最多能到达的点。然后我们就可以跑一边bfs来找出李首都最近的需要覆盖的点,再贪心,将能能到达首都的军队与需要覆盖的点匹配。
大概就是这样,具体的详见代码
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define N 100005 typedef long long ll; using namespace std; struct edge { int from,to,next,spe; }f[N<<1]; struct node1 { ll nfill,nval; friend bool operator<(node1 x,node1 y) { return x.nval<y.nval; } }h1[N]; struct node2 { ll arri,vala; friend bool operator<(node2 x,node2 y) { return x.vala<y.vala; } }h2[N]; int first[N],arm[N],last=0,n,m,flag[N]; ll grand[N][20],dis[N][20],deep[N]; int flagg[N],arri[N],vala[N],cnt1,cnt2; void addedge(int x,int y,int z) { ++last; f[last].from=x; f[last].spe=z; f[last].to=y; f[last].next=first[x]; first[x]=last; } inline int read() { char ch=getchar(); int out=0,flagn=1; while(ch<'0'||ch>'9') { if(ch=='-') { flagn=-1; } ch=getchar(); } while(ch>='0'&&ch<='9') { out=out*10+ch-'0'; ch=getchar(); } return out*flagn; } void Init() { int i,x,y,z; memset(first,-1,sizeof(first)); memset(arm,0,sizeof(arm)); memset(deep,-1,sizeof(deep)); memset(flag,0,sizeof(flag)); memset(grand,0,sizeof(grand)); memset(arri,0,sizeof(arri)); memset(vala,0,sizeof(vala)); // scanf("%d",&n); n=read(); for(i=1;i<=n-1;i++) { // scanf("%d%d%d",&x,&y,&z); x=read(); y=read(); z=read(); addedge(x,y,z); addedge(y,x,z); } // scanf("%d",&m); m=read(); for(i=1;i<=m;i++) { // scanf("%d",&arm[i]); arm[i]=read(); } } void dfs(ll k) { // printf("%d \n",k); int i,j; flag[k]=1; for(i=1;i<=deep[k];i++); i--; for(j=1;j<=i;j++) { grand[k][j]=grand[grand[k][j-1]][j-1]; dis[k][j]=dis[grand[k][j-1]][j-1]+dis[k][j-1]; } for(i=first[k];i!=-1;i=f[i].next) { if(!flag[f[i].to]) { deep[f[i].to]=deep[k]+1; grand[f[i].to][0]=k; dis[f[i].to][0]=1ll*f[i].spe; dfs(f[i].to); } } } void dfs1(ll k)//遍历所有节点查看是否有军队镇守,如果有一支军队只能达到根节点下两层,那么根节点下 一层的节点就需要派遣另一支军队 { if(flagg[k]) return ; int i,num=0; for(i=first[k];i!=-1;i=f[i].next) { if(f[i].to!=grand[k][0]) { dfs1(f[i].to); if(!flagg[f[i].to]&&k!=1) { num=0; return ; } else { num=1; } } } if(num==1) { flagg[k]=1; } return ; } int judge(ll x) { memset(flagg,0,sizeof(flagg)); /* if(x==8) { system("pause"); } printf("%lld\n",x); */ int i,j,k; cnt1=0,cnt2=0; for(k=1;k<=m;k++) { ll q=arm[k],p=0; // for(i=1;i<=deep[q];i++); i--; for(j=17;j>=0;j--) { if(grand[q][j]&&dis[q][j]+p<=x) { p+=dis[q][j]; q=grand[q][j]; } } //在倍增时就已经找到各个军队能到达的最高点了 //如果不能到达1,那这支军队能发挥的最大作用就是他所到达的最高节点 //如果这支军队能到达1,那么就记录该军队能到达的离1最近的城市和剩余的时间 if(q!=1) { flagg[q]=1; } else { ++cnt1; h2[cnt1].vala=x-p; q=arm[k]; for(j=17;j>=0;j--) { if(grand[q][j]>1) { q=grand[q][j]; } } h2[cnt1].arri=q; } } dfs1(1); for(i=first[1];i!=-1;i=f[i].next) { if(!flagg[f[i].to]) { ++cnt2; h1[cnt2].nfill=f[i].to; h1[cnt2].nval=f[i].spe; } } if(cnt1<cnt2) { return 0; } //h1为需要填补的地点,h2为能到达首都的军队的当前地点 sort(h1+1,h1+cnt2+1); sort(h2+1,h2+cnt1+1); j=1; for(i=1;i<=cnt1;i++) { if(!flagg[h2[i].arri]) { flagg[h2[i].arri]=1; } else { if(h2[i].vala>=h1[j].nval) { flagg[h1[j].nfill]=1; j++; } } while(flagg[h1[j].nfill]&&j<=cnt2) j++; } // while(flagg[h1[j].nfill]) j++; if(j<=cnt2) { return 0; } return 1; } void work() { deep[1]=0; dfs(1); ll l=0,r=150000; while(l<r) { ll mid=l+(r-l)/2; if(judge(mid)) { r=mid; } else { l=mid+1; } } printf("%lld",l); } int main() { // freopen("testdata.in","r",stdin); Init(); work(); return 0; }