天天看點

2018_9_23 模拟賽前言:JZOJ 5775 農夫約的假期分析代碼JZOJ 5776 小x遊世界樹JZOJ 5786 觀察後續

今日比賽

  • 前言:
  • JZOJ 5775 農夫約的假期
    • 題目
  • 分析
  • 代碼
  • JZOJ 5776 小x遊世界樹
    • 題目
    • 分析
    • 代碼
  • JZOJ 5786 觀察
    • 題目
    • 分析
    • 代碼
  • 後續

前言:

感覺自己越來越渺小了

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;
    }
           

後續