从第一场到今天的第五场,每场都有那么些无奈吧,跟去年一样,还是最后一场顺手。。。肯定是昨晚玩三国杀被虐而爆人品了,其实是因为好久没玩才被虐的。。。
今天做的第一题就是08题 :hdu 4417 Super Mario
这题一拿到,没什么想法,突然想去上厕所= =,期间想出正解,回来后果断贴模板,也就是前几天我做过的划分树。。。
其实就是每次询问区间内小于等于一个数的数有几个,很明显符合划分树的范畴,然后只要改改query函数,没次对于大于中间的数就加上划分到左边的个数,否则不管,有个问题要处理下,就是可能区间会划分到空,还有可能只有一个元素,而且等于查找的数,要特殊处理下。。。
贴下代码:
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int mm=111111; int a[mm],s[22][mm],g[22][mm]; int i,j,k,n,m,t,ans,cs=0; void build(int l,int r,int d) { if(l==r)return; int i,t1,t2,same=0,m=(l+r)>>1; for(i=l;i<=r;++i) g[d][i]=0,same+=(s[d][i]<a[m]); same=m-l+1-same; for(t1=i=l,t2=m+1;i<=r;++i) if(s[d][i]==a[m]&&same)--same,s[d+1][t1++]=s[d][i],g[d][i]=1; else if(s[d][i]<a[m]) s[d+1][t1++]=s[d][i],g[d][i]=1; else s[d+1][t2++]=s[d][i]; for(i=l;i<r;++i) g[d][i+1]+=g[d][i]; build(l,m,d+1); build(m+1,r,d+1); } void query(int L,int R,int k,int l,int r,int d) { if(L>R)return; if(l==r) { if(a[l]<=k)++ans; return; } int m=(l+r)>>1,w1=0,w2=g[d][R]; if(L>l)w2-=(w1=g[d][L-1]); if(a[m]>k)query(l+w1,l+w1+w2-1,k,l,m,d+1); else { ans+=w2; w1=L-l-w1; w2=R-L+1-w2; query(m+w1+1,m+w1+w2,k,m+1,r,d+1); } } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(i=1;i<=n;++i) { scanf("%d",&a[i]); s[0][i]=a[i]; } sort(a+1,a+n+1); build(1,n,0); printf("Case %d:\n",++cs); while(m--) { scanf("%d%d%d",&i,&j,&k); ++i,++j; ans=0; query(i,j,k,1,n,0); printf("%d\n",ans); } } return 0; }
好吧,这题看完后就是个水题,直接把相邻的用flood fill标记,并记录这个块有几格,长度为多少,宽度为多少,保证个数大于5,长和宽相等,并且在对应的四个位置都是#就行,也就是这个块的第一行和最后一行的中间,第一列和最后一列的中间。。。
代码:
#include<cstdio> #include<iostream> using namespace std; const int mm=55; int dx[]={0,0,-1,1}; int dy[]={-1,1,0,0}; char map[mm][mm]; int qx[mm*mm],qy[mm*mm]; int n,ans; void bfs(int x,int y) { int i,ml=y,mr=y,mt=x,mb=x,s=1,l,r=1; qx[0]=x,qy[0]=y,map[x][y]='-'; for(l=0;l<r;++l) for(i=0;i<4;++i) { x=qx[l]+dx[i]; y=qy[l]+dy[i]; if(x<1||x>n||y<0||y>=n||map[x][y]!='#')continue; qx[r]=x,qy[r]=y,map[x][y]='-',++r; ml=min(ml,y); mr=max(mr,y); mt=min(mt,x); mb=max(mb,x); ++s; } if(mr-ml==mb-mt&&s>4&&s==(mr-ml+mb-mt+1) &&map[mt][(ml+mr)>>1]=='-' &&map[mb][(ml+mr)>>1]=='-' &&map[(mt+mb)>>1][ml]=='-' &&map[(mt+mb)>>1][mr]=='-')++ans; } int main() { int i,j; while(scanf("%d",&n),n) { for(i=1;i<=n;++i) scanf("%s",map[i]); ans=0; for(i=1;i<=n;++i) for(j=0;j<n;++j) if(map[i][j]=='#')bfs(i,j); printf("%d\n",ans); } return 0; }
后来选了02题 hdu 4411 Arrest
这题一开始看挺麻烦,不过马上想到一个费用流模型,首先构造个源点还有一个汇点,并把每个城市都分成两个点i和i‘,由于每次只能按编号小的开始抓,所以直接在i’到j (j>i)连上一条边,容量为1,费用为i到j的最短路。。,对于么个城市i连上0到i的变,容量为1,费用为0到i的最短路,再连上一条从i‘到汇点的边,容量为1,费用为i到0的最短路,表示回到0,然后从i到i’连上一条容量为1,费用为很大的负数,这样就保证肯定通过这条边,也就是抓了这个城市的人,最后连一条边从源点到0,容量为k,费用为0
到这里,我以为就对了,结果发现样例都过不了,多亏了这组样例啊,后来发现这样写,k个组员都必须出去走走,所有不是最优的,实际上有的组员不用出去的= =
解决方法很简单,在0和汇点直接连一条容量为k费用为0的边,表示组员呆着不动。。。
代码:
#include<cstdio> #include<iostream> using namespace std; const int mm=666666; const int mn=222; const int oo=1e9; int src,dest,node,edge; int ver[mm],cost[mm],flow[mm],next[mm]; int head[mn],dis[mn],p[mn],q[mn]; int d[mn][mn]; bool vis[mn]={0}; void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0;i<node;++i)head[i]=-1; edge=0; } void addedge(int u,int v,int f,int c) { ver[edge]=v,flow[edge]=f,cost[edge]=c,next[edge]=head[u],head[u]=edge++; ver[edge]=u,flow[edge]=0,cost[edge]=-c,next[edge]=head[v],head[v]=edge++; } bool Spfa() { int i,u,v,l,r=0,tmp; for(i=0;i<node;++i)dis[i]=oo; dis[q[r++]=src]=0; p[src]=p[dest]=-1; for(l=0;l!=r;(++l==mn)?l=0:l) for(i=head[u=q[l]],vis[u]=0;i>=0;i=next[i]) if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i])) { dis[v]=tmp; p[v]=i^1; if(vis[v])continue; vis[q[r++]=v]=1; if(r==mn)r=0; } return p[dest]>-1; } int Spfaflow() { int i,delta,ret=0; while(Spfa()) { for(i=p[dest],delta=oo;i>=0;i=p[ver[i]]) if(flow[i^1]<delta)delta=flow[i^1]; for(i=p[dest];i>=0;i=p[ver[i]]) flow[i]+=delta,flow[i^1]-=delta; ret+=delta*dis[dest]; } return ret; } int main() { int i,j,k,n,m,w; while(scanf("%d%d%d",&n,&m,&w),n+m+w) { prepare(n+n+3,n+n+1,n+n+2); for(i=0;i<=n;++i) for(j=0;j<=n;++j) d[i][j]=1e6; while(m--) { scanf("%d%d%d",&i,&j,&k); d[i][j]=d[j][i]=min(d[i][j],k); } for(k=0;k<=n;++k) for(i=0;i<=n;++i) for(j=0;j<=n;++j) d[i][j]=min(d[i][j],d[i][k]+d[k][j]); addedge(src,0,w,0); addedge(0,dest,w,0); for(i=1;i<=n;++i) { addedge(0,i,1,d[0][i]); addedge(i,i+n,1,-1e7); addedge(i+n,dest,1,d[0][i]); } for(i=1;i<=n;++i) for(j=i+1;j<=n;++j) addedge(i+n,j,1,d[i][j]); printf("%d\n",Spfaflow()+n*10000000); } return 0; }
就是10题 hdu 4419 Colourful Rectangle
这题一看似曾相识,赶紧翻翻以前做过的题,确实有比较相似的,不过这题不大一样,以前是统计不相同的个数,这次是统计颜色的面积,不过也好处理,剩下就是线段树了
首先离散化y轴,然后扫描线从x最小到最大,逐步插入线段,线段分插入和删除,线段树组要保存当前线段其中颜色的长度,还有当前线段图上的颜色的个数,颜色的计算方法用二进制,R个数大于0就加1,否则加0,G大于0就加2,B加4,。。。加完的值就是当前线段图上的颜色,具体还得和子线段合并,如果子线段没有颜色,那么当前线段就只有一种颜色,长度为线段长度,如果子线段有颜色,并且这个颜色并上当前线段的颜色,改变了颜色,就减少当前线段的颜色,增加新的颜色的长度,具体看代码吧:
PS:这题本来应该这样就过了,但是我调试了近半个小时,找不到错,最后把统计答案从double改成long long,居然就过了,具体我也找不到原因。。。
总之被坑了近半个点;
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define ls rt<<1 #define rs rt<<1|1 #define lson l,m,ls #define rson m,r,rs using namespace std; const int mm=22222; const int mn=mm<<2; int ot[]={1,2,4,3,5,6,7}; struct seg { int x,y1,y2,val; }g[mm]; int len[mn][8],y[mm],L,R; long long ans[8]; int t[mn][4],val; void build() { memset(len,0,sizeof(len)); memset(t,0,sizeof(t)); memset(ans,0,sizeof(ans)); } void updata(int l,int r,int rt) { if(L<=y[l]&&R>=y[r])val<0?--t[rt][-val]:++t[rt][val]; else { int m=(l+r)>>1; if(L<y[m])updata(lson); if(R>y[m])updata(rson); } int ste=(t[rt][1]>0)|((t[rt][2]>0)<<1)|((t[rt][3]>0)<<2); if(ste>0) { for(int i=1;i<8;++i)len[rt][i]=0; len[rt][ste]=y[r]-y[l]; for(int i=1;i<8;++i) if((i|ste)!=ste) { int tmp=(len[rt<<1][i]+len[rt<<1|1][i]); len[rt][i|ste]+=tmp; len[rt][ste]-=tmp; } } else if(l>=r)memset(len[rt],0,sizeof(len[rt])); else for(int i=1;i<8;++i)len[rt][i]=(len[rt<<1][i]+len[rt<<1|1][i]); } bool cmp(seg a,seg b) { return a.x<b.x; } int main() { int i,j,m,n,t,cs=0; char co[8]; scanf("%d",&t); while(t--) { scanf("%d",&n); for(i=0;i<n;++i) { scanf("%s%d%d%d%d",co,&g[i].x,&y[i],&g[i+n].x,&y[i+n]); if(co[0]=='R')j=1; if(co[0]=='G')j=2; if(co[0]=='B')j=3; g[i].y1=y[i],g[i].y2=y[i+n],g[i].val=j; g[i+n].y1=y[i],g[i+n].y2=y[i+n],g[i+n].val=-j; } sort(y,y+n+n); sort(g,g+n+n,cmp); for(m=i=0;i<n+n;++i) if(y[m]<y[i])y[++m]=y[i]; build(); for(i=0;i<n+n;++i) { L=g[i].y1,R=g[i].y2,val=g[i].val; updata(0,m,1); if(g[i].x<g[i+1].x) for(j=1;j<8;++j) ans[j]+=(long long)(g[i+1].x-g[i].x)*(long long)len[1][j]; } printf("Case %d:\n",++cs); for(j=1;j<8;++j) printf("%I64d\n",ans[ot[j-1]]); } return 0; }
不过大概是注定要悲剧的,搞到一半就发现没法搞了,然后只能继续随便写写,做做样子了。。。
今天总体上发挥良好,把所有水题都A掉了,呵呵
网络赛到此全部终结,但是一切才刚刚开始,今年或许是最后一年,希望一切顺利。。。