2015编程之美复赛
第一题,不道是什么鬼。。
第二题猜数字。
很多用主席树,我不会,啊啊啊~~~~记得这题有出过吧,想了一发线段树的,把所有的数排序,同时把询问K排序,
做两发遍历,首先从小到大遍历所有的数,单点更新比K小的线段树的点,维护最大值,遇到>=K时则查询一发。
再从大到小遍历,更新比k大的,维护最小值,遇<=K时就查询。比较即可。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N= 200020; const int inf=1000000099; int seg[N*4]; struct No{ int val,pos; }node[N]; struct Qu{ int l,r,k,index; }query[N]; int tree_index[N]; int ans[N]; bool cmp1(No a,No b){ if(a.val<b.val) return true; return false; } bool cmp2(Qu a,Qu b){ if(a.k<b.k) return true; return false; } int n,q; void build1(int rt,int l,int r){ seg[rt]=-inf; if(l==r){ tree_index[l]=rt; return ; } int mid=(l+r)>>1; build1(rt<<1,l,mid); build1(rt<<1|1,mid+1,r); } void update1(int rt,int val){ seg[rt]=val; rt/=2; while(rt){ seg[rt]=max(seg[rt<<1],seg[rt<<1|1]); rt/=2; } } void build2(int rt,int l,int r){ seg[rt]=inf; if(l==r){ tree_index[l]=rt; return ; } int mid=(l+r)>>1; build2(rt<<1,l,mid); build2(rt<<1|1,mid+1,r); } void update2(int rt,int val){ seg[rt]=val; rt/=2; while(rt){ seg[rt]=min(seg[rt<<1],seg[rt<<1|1]); rt/=2; } } int queryMax(int rt,int l,int r,int L,int R){ if(l<=L&&R<=r){ return seg[rt]; } int mid=(L+R)>>1; if(l>=mid+1){ return queryMax(rt<<1|1,l,r,mid+1,R); } else if(r<=mid) return queryMax(rt<<1,l,r,L,mid); return max(queryMax(rt<<1,l,r,L,mid),queryMax(rt<<1|1,l,r,mid+1,R)); } int queryMin(int rt,int l,int r,int L,int R){ if(l<=L&&R<=r){ return seg[rt]; } int mid=(L+R)>>1; if(l>=mid+1){ return queryMin(rt<<1|1,l,r,mid+1,R); } else if(r<=mid) return queryMin(rt<<1,l,r,L,mid); return min(queryMin(rt<<1,l,r,L,mid),queryMin(rt<<1|1,l,r,mid+1,R)); } int main(){ int T,icase=0; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&q); for(int i=1;i<=n;i++){ scanf("%d",&node[i].val); node[i].pos=i; } for(int i=1;i<=q;i++){ scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].k); query[i].index=i; ans[i]=inf; } sort(node+1,node+1+n,cmp1); sort(query+1,query+1+q,cmp2); build1(1,1,n); int j=1; for(int i=1;i<=q;i++){ for(;node[j].val<=query[i].k&&j<=n;j++){ update1(tree_index[node[j].pos],node[j].val); } int t=queryMax(1,query[i].l,query[i].r,1,n); if(t==-inf) continue; ans[query[i].index]=min(ans[query[i].index],query[i].k-t); } // for(int i=1;i<=q;i++) // printf("%d\n",ans[i]); j=n; build2(1,1,n); for(int i=q;i>0;i--){ for(;node[j].val>=query[i].k&&j>0;j--){ update2(tree_index[node[j].pos],node[j].val); } int t=queryMin(1,query[i].l,query[i].r,1,n); if(t==inf) continue; ans[query[i].index]=min(ans[query[i].index],t-query[i].k); } printf("Case #%d:\n",++icase); for(int i=1;i<=q;i++) printf("%d\n",ans[i]); } return 0; }
第三题,机器人。
感谢斌神。知道,对于同一种颜色,归并后相对顺序是不对的。处理出对某个颜色相对于某颜色,所有块要移动到最后位置经过的步数,斌神叫逆序。然后,优化一下,对于某种颜色,它之前已加入K种颜色(状态压缩),它要移动到最后,经过的步数。枚举各种状态dp[k]为该状态变颜色相连要的步数,枚举状态内的颜色,求出在该状态下,该颜色移动到最末所需的最少步数,比较。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define LL long long using namespace std; const LL inf=1100000000000LL; const int MAXN=100050; int robot[MAXN]; LL dp[1<<17]; int b[20]; LL num[20][20]; LL f[20][1<<17]; int bit[20],loc[1<<17]; int main(){ int T,icase=0; bit[0]=1; loc[bit[0]]=0; for(int i=1;i<17;i++){ bit[i]=(bit[i-1]*2); loc[bit[i]]=i; } scanf("%d",&T); while(T--){ int n,k; scanf("%d%d",&n,&k); for(int i=0;i<n;i++){ scanf("%d",&robot[i]); robot[i]--; } memset(b,0,sizeof(b)); memset(num,0,sizeof(num)); for(int i=n-1;i>=0;i--){ for(int j=0;j<k;j++){ if(robot[i]!=j){ num[robot[i]][j]+=b[j]; } } b[robot[i]]++; } int tot=(1<<k); for(int j=0;j<k;j++){ f[j][0]=0; for(int i=1;i<tot;i++){ int t=i&(-i); f[j][i]=f[j][i^t]+num[j][loc[t]]; } } for(int i=0;i<tot;i++) dp[i]=inf; dp[0]=0; for(int i=0;i<tot;i++){ if(dp[i]==inf) continue; for(int j=0;j<k;j++){ if(bit[j]&i) continue; dp[i|bit[j]]=min(dp[i|bit[j]],dp[i]+f[j][i]); } } printf("Case #%d: %lld\n",++icase,dp[tot-1]); } return 0; }
第四题。可以知道,若城市被包塔包围,则它至多在一个三角形内。而激活塔的费用远小于城市的,所以优先激活塔。对塔求一个凸包,看哪些点在凸包内,然后计算塔的最小环能把那些城市全包含,floyd即可,黙认为逆时针即可,计算那些城市是否在边的左边,在则可以连边,不在就不能。然后就计算即可。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int MAXN=110; const int inf=10000000; struct Point{ int x,y; }cities[MAXN],tower[MAXN],anscity[MAXN]; int n,m,top,g,p; int pexres[MAXN]; bool cmp(Point a,Point b){ if(a.y==b.y) return a.x<b.x; return a.y<b.y; } bool multi(Point sp,Point ep,Point op){ return (sp.x-op.x)*(ep.y-op.y)>=(ep.x-op.x)*(sp.y-op.y); } int Multi(Point p1,Point p2,Point p0){ return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); } int inside(int index){ int i; int now; for(i=0;i<top;i++){ now=Multi(tower[pexres[i]],tower[pexres[(i+1)%top]],cities[index]); if(now<=0) return 0; } return 1; } void Graham(){ int i,len; top=1; sort(tower,tower+n,cmp); if(n==0) return ; pexres[0]=0; if(n==1) return ; pexres[1]=1; if(n==2) return ; pexres[2]=2; for(i=2;i<n;i++){ while(top&&multi(tower[i],tower[pexres[top]],tower[pexres[top-1]])) top--; pexres[++top]=i; } len=top; pexres[++top]=n-2; for(i=n-3;i>=0;i--){ while(top!=len&&multi(tower[i],tower[pexres[top]],tower[pexres[top-1]])) top--; pexres[++top]=i; } } int d[MAXN][MAXN]; bool check(int i,int j,int cnt){ for(int k=0;k<cnt;k++){ int now=Multi(tower[i],tower[j],anscity[k]); if(now<=0) return false; } return true; } int main(){ int T,icase=0; scanf("%d",&T); while(T--){ scanf("%d%d%d%d",&n,&m,&g,&p); for(int i=0;i<n;i++){ scanf("%d%d",&tower[i].x,&tower[i].y); } for(int i=0;i<m;i++) scanf("%d%d",&cities[i].x,&cities[i].y); printf("Case #%d: ",++icase); if(n<3){ printf("%d\n",g*m); continue; } Graham(); // for(int i=0;i<=top;i++) // cout<<pexres[i]<<endl; int cnt=0; for(int i=0;i<m;i++){ if(inside(i)){ anscity[cnt++]=cities[i]; } } int len=inf; if(cnt){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ d[i][j]=inf; } } for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(i==j) continue; if(check(i,j,cnt)){ d[i][j]=1; } } } for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ for(int k=0;k<n;k++){ d[i][j]=min(d[i][j],d[i][k]+d[k][j]); } } } for(int i=0;i<n;i++) len=min(len,d[i][i]); } if(cnt==0) len=0; printf("%d\n",(m-cnt)*g+p*len); } return 0; }