noip 2018 模拟赛18

T 1 T_1 T1——num(2032)

Description:
求第 n n n个刚好有 k k k 1 1 1的二进制正整数。
n ≤ 1 0 30 , 2 ≤ k ≤ 20 n\le10^{30},2\le k\le20 n1030,2k20,答案长度不超过 1000 1000 1000

Solution:

  • 比较常规的组合数,对于每一位 i i i,我们判断 C n − 1 i − 1 C_{n-1}^{i-1} Cn1i1是否大于当前的值即可。
  • 再套一个高精度。

Code:

#include
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i=i##_end_;--i)
#define ll long long
templateinline bool chkmin(T&x,T y){return x>y?x=y,1:0;}
templateinline bool chkmax(T&x,T y){return xinline void rd(T&x){
	x=0;char c;
	while((c=getchar())<48);
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
}

const int N=302;

int K;

#define L 255
#define P 10000
 
struct Big{
    int num[L],len;
    Big(){
        memset(num,0,sizeof(num));
        len=1;
    }
    void Rd(){
        char A[N];
        scanf("%s",A);
        int SL=strlen(A);
        len=0;
        for(int i=SL-1,res;res=0,i>=0;num[len++]=res,i-=4){
            if(i>=3)for(int j=i-3;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
            else for(int j=0;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
        }
    }
    void Pr(){
      	if(!num[len-1]){putchar("0");return;}
        printf("%d",num[len-1]);
        DREP(i,len-2,0)printf("%04d",num[i]);       
    }
    Big operator +(const Big &a)const{
        Big b;
        b.len=max(len,a.len);
        REP(i,0,b.len-1){
            int &B=b.num[i];
            B+=num[i]+a.num[i];
            if(B>=P)B-=P,b.num[i+1]++;
        } 
        if(b.num[b.len])b.len++;
        return b;
    }
    Big operator+(int a){
        Big b;
        b.len=len;
        REP(i,0,len-1){
            int &B=b.num[i];
            B+=num[i]+a%P;
            if(B>=P)b.num[i+1]++,B-=P;
            a/=P;
        }
        if(b.num[b.len])b.len++;
        return b;
    }
    bool operator<=(const Big &a)const{
        if(len!=a.len)return lenpos[i+1])pos[i]--,putchar('0');
    }
    putchar('\n');
	
	return 0;
}

T 2 T_2 T2——dreaming

Description:

n n n个点 m m m条边的图,现在加 n − 1 − m n-1-m n1m条长度为 L e n Len Len的边使之变为一棵树,求树的直径的最小值。
n ≤ 1 0 5 n\le10^5 n105

Solution:

  • 比较裸的题了…
  • 初始,我们预处理出每个树的直径,求出直径上最佳的中点 m i d mid mid( d = m i n { m a x { d i s [ m i d ] , d i s [ R ] − d i s [ m i d ] } } d=min\{max\{dis[mid],dis[R]-dis[mid]\}\} d=min{max{dis[mid],dis[R]dis[mid]}}),拿这些中点来连边。
  • 最优的连边策略,一定是拿拥有最长的 d d d的中点与其它中点建边。
  • 建完边后再求一遍直径即为答案。

Code:

#include
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i=i##_end_;--i)
#define ll long long
templateinline bool chkmin(T&x,T y){return x>y?x=y,1:0;}
templateinline bool chkmax(T&x,T y){return xinline void rd(T&x){
	x=0;char c;
	while((c=getchar())<48);
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
}

const int N=1e5+2,INF=0x3f3f3f3f;

int n,m;
ll Len;

int qwq,head[N];
struct edge{
	int to,nxt;
	ll w;
}E[N<<1];
void addedge(int x,int y,ll z){E[qwq]=(edge){y,head[x],z};head[x]=qwq++;}
#define EREP(x) for(int i=head[x];~i;i=E[i].nxt)

struct p100{
	
	ll dis[N];
	int fa[N];
	ll Mn[N];// 每个小树的 min{max{dis[L][mid],dis[mid][R]}} 
	int rt[N],cnt;// 每个小树的中点 
	
	bool vis[N];
	
	int Id;
	ll Mx;
	
	void dfs1(int x,int f,ll d){
		if(chkmax(Mx,d)) Id=x;
		vis[x]=1;
		EREP(x){
			int y=E[i].to;
			if(y==f)continue;
			dfs1(y,x,d+E[i].w);
		}
	}
	
	void dfs2(int x,int f,ll d){
		if(chkmax(Mx,d)) Id=x;
		fa[x]=f;
		dis[x]=d;
		EREP(x){
			int y=E[i].to;
			if(y==f)continue;
			dfs2(y,x,d+E[i].w);
		}
	}
	
	void solve(){
		REP(i,1,n) if(!vis[i]) {
			Id=Mx=-1;
			dfs1(i,0,0);
			rt[++cnt]=Id;//L
			Mx=-1;
			dfs2(Id,0,0);//R
			int R=Id,L=rt[cnt],mid=R;
			Mn[cnt]=INF;
			if(L==R) Mn[cnt]=0;
			while(mid!=L){
				if(chkmin(Mn[cnt],max(dis[mid],dis[R]-dis[mid])))rt[cnt]=mid;
				mid=fa[mid];
			}
		}
		
		ll mx1=-1;
		int Rt1=-1;
		
		REP(i,1,cnt)if(chkmax(mx1,Mn[i])) Rt1=rt[i];
		
		REP(i,1,cnt){
			if(Rt1==rt[i])continue;
			addedge(Rt1,rt[i],Len);
			addedge(rt[i],Rt1,Len);
		}
		
		Mx=Id=-1;
		dfs1(1,0,0);
		Mx=-1;
		dfs2(Id,0,0);
		printf("%lld\n",Mx);
		
	}
}p2;

int main(){
//	freopen("dreaming.in","r",stdin);
//	freopen("dreaming.out","w",stdout);
	rd(n),rd(m),rd(Len);
	memset(head,-1,sizeof head);
	REP(i,1,m){
		int a,b,c;
		rd(a),rd(b),rd(c);
		++a,++b;
		addedge(a,b,c);
		addedge(b,a,c);
	}
	
	p2.solve();
	return 0;
}

T 3 T_3 T3——foodshop

Description:

n n n个点, n n n条边的图,求一个点,使得这个点到其它的点的最大距离最小(注意:这个点可以在边上)。
n ≤ 1 0 5 , L i ≤ 1 0 9 n\le10^5,L_i\le10^9 n105,Li109

Solution:

  • 因为它是一个 n n n个点 n n n条边的图。
  • 那么它是一个基环树。
  • 首先,一定会先考虑分类讨论。我们优先解决环上。
  • 对于点在环上时,我们可以发现,最大距离一定是环上与该点对立的点的距离+对立点的子树的最大深度,且这种对立点至多会有2个。
  • 那么考虑对立点有2个时,发现这两个对立点之间的边使无用的,即可以删除。
  • 那么由此启发,我们可以枚举一条环边删除,那么答案即为剩下的这棵树的直径除2。
  • 但枚举环边,这样复杂度是 Θ ( n 2 ) \Theta(n^2) Θ(n2)的。
  • 那么我们考虑数据结构来优化。
  • 对于一条要删除的环边 ( k , k + 1 ) (k,k+1) (k,k+1),我们找直径,由两次dfs找直径启发,我们从 k k k点向左找最远距离的点,再从该点找最远的距离,同理,从 k + 1 k+1 k+1点向右找。
  • 那么由环上距离,我们知道 D i s t ( i , j ) = m x [ i ] + m x [ j ] + { d i s [ j ] − d i s [ i ] , i ≤ j L e n − d i s [ j ] + d i s [ i ] , i > j Dist(i,j)=mx[i]+mx[j]+\begin{cases} dis[j]-dis[i],i\le j \\ Len-dis[j]+dis[i],i>j\end{cases} Dist(i,j)=mx[i]+mx[j]+{dis[j]dis[i],ijLendis[j]+dis[i],i>j
  • 我们可以考虑线段树来维护环上每个点到其它点的最大距离。
  • 这样复杂度就是 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)

Code:

#include
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i=i##_end_;--i)
#define ll long long
templateinline bool chkmin(T&x,T y){return x>y?x=y,1:0;}
templateinline bool chkmax(T&x,T y){return xinline void rd(T&x){
	x=0;char c;
	while((c=getchar())<48);
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
}

const int N=2e5+2;

int n;

int qwq,head[N];
struct edge{
	int to,nxt;
	ll w;
}E[N<<1];
void addedge(int x,int y,ll z){E[qwq]=(edge){y,head[x],z};head[x]=qwq++;}
#define EREP(x) for(int i=head[x];~i;i=E[i].nxt)

int dfn[N],low[N],tim;
int stk[N<<1],top;
bool vis[N];

int chain[N],len;
bool belong[N];

ll ans;

void tarjan(int x,int f){
	dfn[x]=low[x]=++tim;
	stk[++top]=x;
	bool flag=1;
	EREP(x){
		int y=E[i].to;
		if(y==f and flag){flag=0;continue;}
		if(!dfn[y]) tarjan(y,x),chkmin(low[x],low[y]);
		else chkmin(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]){
		if(stk[top]!=x){
			do {
				chain[++len]=stk[top];
				belong[stk[top]]=1;
			}while(x!=stk[top--]); 
		}
		else top--;
	}
}

struct p70{
	
	bool mark[N<<1];
	ll Mx,Id;
	
	void dfs1(int x,int f,ll d){
		if(chkmax(Mx,d)) Id=x;
		EREP(x){
			if(mark[i])continue;
			if(E[i].to!=f) dfs1(E[i].to,x,d+E[i].w);
		}
	}
	
	void dfs2(int x,int f,ll d){
		if(chkmax(Mx,d)) Id=x;
		EREP(x){
			if(mark[i])continue;
			if(E[i].to!=f) dfs2(E[i].to,x,d+E[i].w); 
		}
	}
	
	void solve(){
		
		ans=1e18;
		
		REP(p,1,len){
			int x=chain[p],y=chain[p%len+1];
			EREP(x){
				if(E[i].to==y) {
					mark[i]=mark[i^1]=1;
					
					Mx=Id=-1;
					dfs1(1,0,0);
					Mx=-1;
					dfs2(Id,0,0);
					
					chkmin(ans,Mx);
					
					mark[i]=mark[i^1]=0;
				}
			}
		}
		
		printf("%.1lf\n",1.0*ans/2);
	}
}p1;

ll dep[N<<1],dis[N],sum[N<<1];
ll mx[N],Sum[N];

ll Dist(int x,int d){
	if(!~x) return -1e18;
	return dep[x]+((d==1)?-sum[x]:sum[x]);
}

struct p100{
	
	ll dfs(int x,int f,ll d,int id){
		chkmax(dep[id],d);
		mx[x]=Sum[x]=0;
		EREP(x){
			int y=E[i].to;
			if(y==chain[id%len+1]) dis[id]=E[i].w;
			if(y==f or belong[y])continue;
			ll t=dfs(y,x,d+E[i].w,id)+E[i].w;
			chkmax(Sum[x],mx[x]+t);
			chkmax(mx[x],t);
		}
		return mx[x];
	}
	

	struct Tree{
		#define lson L,mid,p<<1
		#define rson mid+1,R,p<<1|1
		#define family tree[p],tree[p<<1],tree[p<<1|1]
		 
		struct node{
			int L,R;
			int id1,id2;
			ll mx1,mx2;
		}tree[N<<2];
		
		void Up(node &A,node L,node R){
			if(L.mx1>R.mx1)A.mx1=L.mx1,A.id1=L.id1;
			else A.mx1=R.mx1,A.id1=R.id1;
			if(L.mx2>R.mx2)A.mx2=L.mx2,A.id2=L.id2;
			else A.mx2=R.mx2,A.id2=R.id2;
		}
		
		void build(int L,int R,int p){
			tree[p].L=L,tree[p].R=R;
			if(L==R){
				tree[p].mx1=Dist(L,1);
				tree[p].mx2=Dist(L,2);
				tree[p].id1=tree[p].id2=L;
				return; 
			} 
			int mid=(L+R)>>1;
			build(lson),build(rson);
			Up(family); 
		}
		
		int query(int L,int R,int p,int op){
			if(L>R)return -1;
			if(tree[p].L==L and tree[p].R==R) return op==1?tree[p].id1:tree[p].id2;
			int mid=(tree[p].L+tree[p].R)>>1;
			if(R<=mid) return query(L,R,p<<1,op);
			else if(L>mid) return query(L,R,p<<1|1,op);
			else {
				int l=query(lson,op),r=query(rson,op);
				return Dist(l,op)>Dist(r,op)?l:r; 
			}
		}
		
	}T; 

	void solve(){
		REP(i,1,len){
			dfs(chain[i],0,0,i);
			sum[i]=sum[i-1]+dis[i-1];
		}
		
		REP(i,1,len){
			dep[i+len]=dep[i];
			if(i>1) sum[i+len]=sum[i+len-1]+dis[i-1];
			else sum[i+len]=sum[len]+dis[len];
		}
		
		ans=1e18;
		
		T.build(1,len<<1,1);
		
		REP(i,1,len){
			int l1=T.query(i,i+len-1,1,1);
			int l2=T.query(i,i+len-1,1,2);
			int tmp;
			ll Mx=0;
			
			if(l1==l2){
				int r1=T.query(i,l1-1,1,1);
				tmp=T.query(l1+1,i+len-1,1,1);
				if(Dist(tmp,1)>Dist(r1,1))r1=tmp;
				
				int r2=T.query(i,l2-1,1,2);
				tmp=T.query(l2+1,i+len-1,1,2);
				if(Dist(tmp,2)>Dist(r2,2))r2=tmp;
				
					
				Mx=Dist(l1,1)+Dist(r2,2);
				chkmax(Mx,Dist(l2,2)+Dist(r1,1));
			}
			else Mx=Dist(l1,1)+Dist(l2,2);
			chkmin(ans,Mx);
		}
		REP(i,1,n) chkmax(ans,Sum[i]);
		
		printf("%.1lf\n",1.0*ans/2);
	}
}p2;

int main(){
//	freopen("foodshop.in","r",stdin);
//	freopen("foodshop.out","w",stdout);
	rd(n);
	memset(head,-1,sizeof head);
	REP(i,1,n){
		int a,b;ll c;
		rd(a),rd(b),rd(c);
		addedge(a,b,c);
		addedge(b,a,c);
	}
	
	tarjan(1,0);

	if(n<=3000) p1.solve();
	else p2.solve();
	
	return 0;
}

你可能感兴趣的:(离线赛-总结)