别问我为什么过去一周了才写博客。。。
没有做出最后一题,是我失了智。
这一周本来说好的计组考试结果收到的通知是计网,可怜我计组复习了两周,计网完全没有复习(预习)。
周四终于考完本学期第一场考试,结果我们班的现场答卷情况让我们信安教研室主任勃然大怒。。
今天上午8点半要去实验室做密码学课程设计,结果睡过头被室友锁宿舍一上午,,,于是,,于是在宿舍颓废了一天。不行,我这么好的学生怎么能颓废呢,好吧,是我的良心在痛,晚上拉上室友图书馆复习,准备明天的校赛!
2017 计蒜之道 初赛 第六场
前三场都没打好,只能靠这场碰碰运气看看能不能晋级。额,维森莫是前三场,初赛前两场去西安参加邀请赛被秦龙酒店坑了一把(去过都知道)。所以从第四场开始打的。
急于过题以为只有三种情况,结果WA了一发,仔细看题原来是我失了智。这么久没打代码,遇到这种shabi模拟题竟然有点焦头烂额。
int main()
{
int n;
while(~scanf("%d",&n))
{
printf("+-----+\n");
if(n<20) printf("| E|\n");
else if(n>=20&&n<60) printf("|- E|\n");
else if(n>=90) printf("|- 4G|\n");
else printf("|- 3G|\n");
n-=20;
for(int i=2; i<=5; i++)
{
if(n>=20)
{
printf("|");
for(int j=1; j<=i; j++)
printf("-");
for(int j=i+1; j<=5; j++)
printf(" ");
printf("|\n");
}
else printf("| |\n");
n-=20;
}
printf("+-----+\n");
}
return 0;
}
这么简单的题我竟然直接想到shabi搜索上去了,结果初始值没赋好还WA了两发,啊啊啊,石乐志。
额,中文题就不介绍题意了。
const int N=1e6+5;
int a[55][55],x[55],y[55],vis[55][55];
int dir[5][2]= {{-1,0},{1,0},{0,1},{0,-1}};
int n,k,m;
struct node
{
int x,y;
};
int bfs(int i,int j)
{
memset(vis,0,sizeof(vis));
node tmp;
tmp.x=x[i];
tmp.y=y[i];
queue<node>q;
vis[x[i]][y[i]]=0;
q.push(tmp);
while(!q.empty())
{
tmp=q.front();
q.pop();
if(tmp.x==x[j]&&tmp.y==y[j]) break;
for(int i=0; i<4; i++)
{
int xx=tmp.x+dir[i][0];
int yy=tmp.y+dir[i][1];
if(a[xx][yy]&&!vis[xx][yy])
{
vis[xx][yy]=vis[tmp.x][tmp.y]+1;
node tmpp;
tmpp.x=xx;
tmpp.y=yy;
q.push(tmpp);
}
}
}
return vis[x[j]][y[j]];
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
memset(a,0,sizeof(a));
int h;
for(int i=1; i<=n; i++)
{
scanf("%d",&h);
for(int j=1; j<=h; j++)
a[i][j]=1;
}
scanf("%d",&m);
for(int i=1; i<=m; i++) scanf("%d%d",&x[i],&y[i]);
int ans=0;
for(int i=1; i<=m; i++)
for(int j=i+1; j<=m; j++)
{
if(bfs(i,j)<=k)
{
ans++;
// printf("%d %d\n",i,j);
}
}
printf("%d\n",ans);
}
return 0;
}
看了这个题的数据范围我才意识到自己被简单版的数据范围坑了,明明有更简单的方法却往复杂的方向走。。
结合题意观察到横向距离是不变的(两栋楼横坐标之差),而纵向距离只需枚举看是否有一条通道连接这两栋楼,我们开个数组标记一下就好了。关键要看到H最多才20,即空间复杂度1e5*20。枚举任意两栋楼然后暴力判断即可。
const int N=2e5+5;
int a[N][21],x[2001],y[2001],vis[N][20],h[N];
int n,k,m;
int main()
{
while(~scanf("%d%d",&n,&k))
{
memset(a,0,sizeof(a));
memset(vis,0,sizeof(vis));
h[0]=0;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=h[i-1]; j++)
vis[i][j]+=a[i-1][j];
scanf("%d",&h[i]);
for(int j=1; j<=h[i]; j++)
{
a[i][j]=1;
vis[i][j]++;
}
}
scanf("%d",&m);
for(int i=1; i<=m; i++) scanf("%d%d",&x[i],&y[i]);
int ans=0;
for(int i=1; i<=m; i++)
for(int j=i+1; j<=m; j++)
{
int tmp=abs(x[j]-x[i]);
int h2=max(y[i],y[j]);
int f=y[j]+y[i]-2;
for(int l=2;l<=h2;l++)
if(abs(vis[x[i]][l]-vis[x[j]][l])==tmp)
f=min(f,abs(y[i]-l)+abs(y[j]-l));
tmp+=f;
if(tmp<=k) ans++;
}
printf("%d\n",ans);
}
return 0;
}
还是一样的题意,只不过数据范围变了,注意到和中等版本不同的是这个题的核心办公室可达2e5。那么真正有趣的来了,暴力枚举任意两栋楼肯定是不行的,那么怎么优化呢。我竟然想到公共祖先问题上,结果发现解决不了。。套路,又是这个套路,我们观察到,从左往右只要我们知道了第一栋楼可行解的范围也就是右端点的位置,那么第二栋楼不就在第一栋楼的基础上再往右贪心即可。大牛可能一眼就明白一个滑动窗即可,然而我等菜鸟竟然直接想到二分上了,枚举左端点二分右端点,我怕会超时竟然用上次在玲珑杯学到的一个技巧倍增优化。嗯,越想感觉越对,在这条路上一去不复返。。到最后也没写出来,好吧,是我菜。讲道理,这个二分套路应该是可以的。不过犯了关键错误是我们肯定要对这些点排序,我们想到的是按xy排序,后来发现应该按x+y的大小排序,举个例子就懂了,(1,5)到(2,18)和(3,1)。所以应该按x+y排序。清醒后才发现可以直接尺取贪心。。今天写了一波,感觉过TLE,然而过了,,,,过了。。。核心判断还是和第二题一样。
const int N=2e5+5;
int a[N][21],vis[N][21],h[N];
int n,k,m;
struct node
{
int x,y;
} b[N];
int cmp(node a,node b)
{
return a.x+a.y<b.x+b.y;
/* 不能这样排序,(1,5)到(2,18)和(3,1)之间的距离是不同的
if(a.x!=b.x) return a.x<b.x;
return a.y<b.y;
*/
}
int judge(int i,int j)
{
int tmp=abs(b[j].x-b[i].x);
int h2=max(b[i].y,b[j].y);
int f=b[j].y+b[i].y-2;
for(int l=2; l<=h2; l++)
if(abs(vis[b[i].x][l]-vis[b[j].x][l])==tmp)
f=min(f,abs(b[i].y-l)+abs(b[j].y-l));
tmp+=f;
return tmp<=k;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
memset(a,0,sizeof(a));
memset(vis,0,sizeof(vis));
h[0]=0;
ll ans=0;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=h[i-1]; j++) vis[i][j]+=a[i-1][j];
scanf("%d",&h[i]);
for(int j=1; j<=h[i]; j++)
{
a[i][j]=1;
vis[i][j]++;
}
}
scanf("%d",&m);
for(int i=0; i<m; i++) scanf("%d%d",&b[i].x,&b[i].y);
sort(b,b+m,cmp);
int j=0;
for(int i=0; i<m; i++)
{
while(j<m&&judge(i,j)) j++;//贪心尺取
int k=j;
while(k>=m||!judge(i,k)) k--;//回退,这个操作本来以为会超时。看来数据不强
ans+=k-i;
}
printf("%lld\n",ans);//注意数据范围
}
return 0;
}