bzoj权限题,可以去洛谷上交
先考虑每个点最大可以放多大的箱子,这需要求出离他最近的障碍距离,
这可以从障碍开始bfs,仔细观察发现要像八个方向bfs。
原题就变成了求两点之间的路径上的最小值,这显然路径在最大瓶颈生成树上最优的。点权改成边权,边权是两点之间的最小值,然后用kurskal求出最小生成树。
然后介绍一个不用树剖的方法,用kurskal求的时候并查集按秩合并,这样可以保持最小生成树的形状,并且树高是 l o g n 2 logn^2 logn2的,求路径最小就可以直接暴力爬lca
详细可以看一下bzoj4668
#include
#include
#include
#include
using namespace std;
#define dd c=getchar()
int read() {int s=0,w=1;char c;while (dd,c>'9' || c<'0') if (c=='-') w=-1;while (c>='0' && c<='9') s=s*10+c-'0',dd;return s*w;}
#undef dd
void write(int x) {if (x<0) x=-x,putchar('-');if (x>=10) write(x/10);putchar(x%10|'0');}
void wln(int x) {write(x);puts("");}
const int N=1005;
const int mx[8]={1,1,1,-1,-1,-1,0,0},my[8]={1,0,-1,1,0,-1,1,-1};
struct edge {
int u,v,w;
bool operator <(edge b) const {
return w>b.w;
}
}e[N*N*2];
int n,Q,cnt;
int dis[N][N],vis[N][N],fa[N*N],W[N*N],dep[N*N];
char s[N][N];
queueqx,qy;
void bfs() {
while (!qx.empty()) {
int x=qx.front(),y=qy.front();
qx.pop();qy.pop();
for (int i=0;i<8;i++) {
int xx=x+mx[i],yy=y+my[i];
if (xx>0 && xx<=n && yy>0 && yy<=n && !vis[xx][yy]) {
dis[xx][yy]=dis[x][y]+1;
vis[xx][yy]=1;
qx.push(xx);qy.push(yy);
}
}
}
}
int mk(int x,int y) {return n*(x-1)+y;}
void add(int u,int v,int w) {
e[++cnt].u=u;e[cnt].v=v;e[cnt].w=w;
}
int find(int x) {return x==fa[x]?x:find(fa[x]);}
int getDep(int x) {return x==fa[x]?1:getDep(fa[x])+1;}
int main() {
// freopen("4793.in","r",stdin);
// freopen("4793.out","w",stdout);
n=read();
for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
memset(dis,60,sizeof dis);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) if (s[i][j]=='#') qx.push(i),qy.push(j),dis[i][j]=0,vis[i][j]=1;
bfs();
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) {
dis[i][j]=min(dis[i][j], min(min(i ,j), min(n-i+1, n-j+1)));
if (dis[i][j]) dis[i][j]=(dis[i][j]-1)*2+1;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) {
if (i1;i++) {
u=find(e[i].u),v=find(e[i].v);
if (u==v) continue;
num--;
if (dep[u]