前言:
感觉自己越来越渺小了
JZOJ 5775 农夫约的假期
题目
在一个平面,有 n n n个点,找出一个点使 n n n个点到该点的曼哈顿距离 ( ∑ a b s ( x i − x j ) + a b s ( y i + y j ) ) (\sum abs(x_i-x_j)+abs(y_i+y_j)) (∑abs(xi−xj)+abs(yi+yj))和最短
分析
显然,这道题讲到这里,那么可以发现中位数就是最优的
。All in all,就讲完了
代码
#include <cstdio>
#include <algorithm>
#define rr register
using namespace std;
int m,x[100001],y[100001];long long ans;
inline int in(){
rr int ans=0; rr char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
int main(){
in(); m=in(); in(); rr int mid=m+1>>1;
for (rr int i=1;i<=m;i++) x[i]=in(),y[i]=in(),ans+=in();
nth_element(x+1,x+mid,x+1+m);//求第mid大
nth_element(y+1,y+mid,y+1+m);
for (rr int i=1;i<=m;i++) ans+=abs(x[i]-x[mid])+abs(y[i]-y[mid]);//计算曼哈顿距离
return !printf("%lld\n%d %d",ans,x[mid],y[mid]);
}
JZOJ 5776 小x游世界树
题目
在一棵树上重新找一个根,使根到各点的距离最短
分析
其实这道题可以说是近乎暴力,但是关于找根可以用二次扫描换根法,感性理解,代码才是最有力的解释工具
代码
#include <cstdio>
#define rr register
struct node{int y,w,next;}e[1400011];
int n,son[700001],ls[700001],k=1,ans1=1;long long ans;
inline int in(){
rr int ans=0; rr char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
inline void add(int x,int y){
rr int w=in();
e[++k]=(node){y,w-son[x],ls[x]}; ls[x]=k;
e[++k]=(node){x,w-son[y],ls[y]}; ls[y]=k;
}
inline void dp(int x,int fa,long long sum){
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].y!=fa) dp(e[i].y,x,sum+e[i].w),son[x]+=son[e[i].y];
ans+=sum; son[x]++;
}
inline void dfs(int x,int fa,long long sum){
for (rr int i=ls[x];i;i=e[i].next)
if (e[i].y!=fa) dfs(e[i].y,x,sum+(n-son[e[i].y])*e[i^1].w-son[e[i].y]*e[i].w);
if (sum<ans||sum==ans&&ans1>x) ans1=x,ans=sum;
}
int main(){
n=in();
for (rr int i=1;i<=n;i++) son[i]=in();
for (rr int i=1;i<n;i++) add(in(),in());
for (rr int i=1;i<=n;i++) son[i]=0;
dp(1,0,0); dfs(1,0,ans);
return !printf("%d\n%lld",ans1,ans);
}
JZOJ 5786 观察
题目
求离当前点LCA最深的编号(可能会被修改)
分析
这道题也就是求欧拉序的前驱和该点,和后继,可以用线段树维护,然后求深度更大的点的编号
代码
#include <cstdio>
#include <cstring>
#define rr register
#define f(i,a,b) for (rr int i=a;i<=b;++i)
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define N 800005
bool v[N],op; int top,dep[N],ne[N],ls[N],sta[N],ola[N],rk[N],st[N],dfn[N],son[N];
int n,q,bas=1,tot,father[N],minx[2100001],maxx[2100001]; char buf[1<<13],*p1,*p2;
inline int in(bool &op){
rr int ans=0; rr char c=getchar();
while ((c<48||c>57)&&c!='-') c=getchar();
if (c=='-') op=1,c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline int lca(int x,int y){
while (son[x]!=son[y]) if (dep[son[x]]>dep[son[y]]) x=father[son[x]]; else y=father[son[y]];
return dep[x]<dep[y]?x:y;
}
signed main(){
n=in(op); q=in(op); while ((bas<<=1)<n+2); bas--;
f(i,2,n) father[i]=in(op); dep[1]=1;
f(i,2,n) ne[i]=ls[father[i]],ls[father[i]]=i;
f(i,1,n) sta[i]=ls[i]; st[top=1]=1;
while (top){//第一次深搜求深度和子树大小以及通过子树大小求欧拉序
rr int x=st[top];
if (!dfn[x]) son[rk[dfn[x]=++tot]=x]=1;
if (sta[x]){
st[++top]=sta[x];
dep[sta[x]]=dep[x]+1;
sta[x]=ne[sta[x]];
continue;
}
son[st[--top]]+=son[x];
if (son[ola[st[top]]]<son[x]) ola[st[top]]=x,v[st[top]]=1;
}
f(i,1,n) sta[i]=ls[i],son[i]=0;
st[top=1]=1; rr int ST=1;
while (top){//现在的son表示的已经是倍增的祖先了
rr int x=st[top];
if (!son[x]) son[x]=ST;
if (v[x]){
v[x]=0,st[++top]=ola[x];
continue;
}
if (sta[x]==ola[x]) sta[x]=ne[sta[x]];
if (sta[x]){
ST=st[++top]=sta[x];
sta[x]=ne[sta[x]];
continue;
}
top--;
}
f(i,1,(bas<<1|1)) minx[i]=n+1,maxx[i]=0;//zkw线段树初始化
while (q--){
rr int x=in(op=0),y=dfn[x]+bas;//跳到叶子节点
if (op){
rr int ans;
if (minx[y]==y-bas) ans=x;//直接找到答案
else{
rr int l=0,r=n+1;
for (rr int i=y>>1;i;y=i,i>>=1) if (i<<1==y) r=min(r,minx[y^1]); else l=max(l,maxx[y^1]);//查找前驱和后继
if (l) l=lca(rk[l],x);
if (r<=n) r=lca(x,rk[r]);
ans=dep[l]<dep[r]?r:l;//深度更深
}
if (ans) print(ans); else putchar(48); putchar(10);
}
else{
if (minx[y]==y-bas) minx[y]=n+1,maxx[y]=0; else minx[y]=maxx[y]=y-bas;//修改该点的最小值和最大值
for (rr int i=y>>1;i;y=i,i>>=1) minx[i]=min(minx[y],minx[y^1]),maxx[i]=max(maxx[y],maxx[y^1]); //单点修改
}
}
return 0;
}