从第一场到今天的第五场,每场都有那么些无奈吧,跟去年一样,还是最后一场顺手。。。肯定是昨晚玩三国杀被虐而爆人品了,其实是因为好久没玩才被虐的。。。
今天做的第一题就是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;
}
过完这题,马上下手做过的最多的 05题 hdu 4414 Finding crosses
好吧,这题看完后就是个水题,直接把相邻的用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;
}
好吧,交上去1Y了,此时排名还行,赶紧继续做,又是选题问题,06题还没做,我一看是贪心,但是不会证明,而且写法复杂,果断不做,直接选了最后一题,也
就是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掉了,呵呵
网络赛到此全部终结,但是一切才刚刚开始,今年或许是最后一年,希望一切顺利。。。