[题解]NOIP2012

Day1

A Vigenère 密码

字符串题,随便模拟一下

B 国王游戏

题目描述

恰逢 H 国国庆,国王邀请 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:「排在该大臣前面的所有人的左手上的数的乘积」除以「他自己右手上的数」,然后向下取整得到的结果

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

分析

手推的时候乘号写成了+,于是挂了,居然还去看了题解,该打

先把所有人按照原来的序列排好,然后对于相邻的两个人,我们发现,交换他们两个人的顺序只会改变他们两个人的值,所以我们贪心地使相邻的两个人的最大值最小,那么这样下来最后得到的答案一定是最优的,大概就是利用了冒泡排序的思想

推一下可以发现(a_i*b_i)小的放在前面更优,那么直接sort后求值,数据很大,要用python高精

然后发现自己高精都不会写??

tags

贪心 高精度

C 开车旅行

题目描述

分析

恩有进步,一不小心瞥见倍增的tag然后就会了,恩不错不错

预处理出从每一个点往后的两个选择。

那么我们发现,对于一个询问我们要快速地知道答案,可能会想到二分,那么要维护向后走几步能到哪里,就可以倍增

然后倍增记录往后走(2^i)轮(A,B各走一次为一轮)到的点和两者的距离就可以了

查询的时候只需要看最多能走几轮,然后看A能不能再挣扎一下

预处理:

自己想的是从后往前维护一个双向链表,还要搞个树状数组查询已经加进来的里面比它小的最大的和比他大的最小的,代码奇长无比还是两只log

过了之后看题解里是从前往后维护一个双向链表,这样只需要删除就好了(我真傻,真的

tags

倍增 链表

总结

其实预处理还是很容易想到的,难的就是快速查询,倍增还是很神奇的呐


Day2

A 同余方程

题目描述

求关于(x)的同余方程 $a x equiv 1 pmod {b} $的最小正整数解。

分析

没有分析

扩欧板子题

B借教室

题目描述

分析

没多想直接写了线段树

后来交luoguT了一个点(究竟是人丑自带大常数还是数据的道德沦丧)

可以二分+差分,复杂度——ei不是还是(O((n+m)logm))嘛,大概常数比较小趴

C 疫情控制

题目描述

分析

转换题意:

求每一个军队到达目标位置的最大用时的最小值

题意转换过来之后就很容易想到二分,反正我是连二分都想不到的人

其实想到二分之后就挺简单了:

考虑对于给定的时间,判断能否在这段时间内达到题目的要求。

我们发现,只要时间足够,继续往上跳一定会比留在当前位置更优,因为它能覆盖更多叶子,所以,对于能跳到的最高点不是1的节点,直接往上跳到可以到达的最高点,然后固定在这里。

由于1结点不能停留,对于可以到1的点我们单独考虑:

先让它们停在1的亲儿子上:

  • 如果这个儿子不需要军队覆盖,即它的子树内的所有叶子节点已经被覆盖了,那么这个军队向外走一定比留在原地更优,所以让它先走到1

  • 否则:记(dis)为这个亲儿子到1的距离,(lef)为这个军队从亲儿子出发还能走多远

    • (lef leq 2*dis):可以证明,它留在这个点一定比往外走更优:

      因为一旦它往外走,就需要一个走到1之后还可以走的距离(geq dis)的军队来覆盖它。而它走到1之后还能走的距离(lef-disleq dis),那么还不如不要帮倒忙,让(geq dis)的点去给其他人送温暖

    • (lef >2*dis):不管这个儿子而去覆盖其它人

最后,就得到了两个数组:

  • 走到1之后可以向下走的军队以及能走的距离
  • 需要被覆盖的1的儿子以及需要的距离

贪心维护就好了

代码

LOJ上T了最后一个点,于是读优么写写常么卡卡就过去了

#include<bits/stdc++.h>
#define rep(X,A,B) for(int X=A;X<=B;X++)
#define tep(X,A,B) for(int X=A;X>=B;X--)
#define LL long long
const int N=300010;
const int M=600010;
const int MX=25;
using namespace std;

int n,m;
int tag[N];
int top=0,tmp=0;
LL dis[N],hav[N],ned[N],INF=0;
int am[N],dep[N],fa[N][MX];
int edge[M],lst[N],nxt[M],wei[M],t=0;

void read(int &x){
	x=0;char c=getchar();
	for(;c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+(c^48);
}

void read(LL &x){
	x=0;char c=getchar();
	for(;c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+(c^48);
}

void ADD(int x,int y,int z){
	edge[++t]=y;nxt[t]=lst[x];lst[x]=t;wei[t]=z;
}

void READ(){
	int u,v,w;
	read(n);
	rep(i,1,n-1){
		read(u);read(v);read(w);
		ADD(u,v,w);
		ADD(v,u,w);
	}
	read(m);
	rep(i,1,m)read(am[i]);
}

void SEARCH(int x,int pa){
	dep[x]=dep[pa]+1;
	fa[x][0]=pa;
	for(int i=1;(1<<i)<dep[x];i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int r=lst[x];r;r=nxt[r]){
		if(edge[r]==pa)continue;
		dis[edge[r]]=dis[x]+wei[r];
		SEARCH(edge[r],x);
	}
	INF=max(INF,dis[x]);
}

void DFS(int x,int pa){
	if(tag[x]==1)return;
	tag[x]=1;
	int hlp=0;
	for(int r=lst[x];r;r=nxt[r]){
		if(edge[r]==pa)continue;
		DFS(edge[r],x);
		tag[x]&=tag[edge[r]];
		hlp++;
	}
	if(!hlp)tag[x]=0;
}

struct nn{
	int pos;
	LL lef;
}Q[N];

int cmp(nn A,nn B){
	if(A.pos==B.pos)return A.lef<B.lef;
	else return A.pos<B.pos;
}

void UPD(int x,LL lef){
	tep(i,20,0){
		if(lef<dis[x]-dis[fa[x][i]])continue;
		if(dep[fa[x][i]]>1){
			lef-=dis[x]-dis[fa[x][i]];
			x=fa[x][i];
		}
	}
	if(fa[x][0]!=1){
		tag[x]=1;
		return;
	}
	Q[++tmp]=(nn){x,lef};
	return;
}

int CAN(){
	sort(hav+1,hav+top+1);
	sort(ned+1,ned+tmp+1);
	if(top<tmp)return 0;
	int pos=1;
	rep(i,1,top){
		if(pos<=tmp&&ned[pos]<=hav[i])pos++;
	}
	return (pos>tmp);
}

int CHK(LL x){
	top=tmp=0;
	rep(i,1,n)tag[i]=0;
	rep(i,1,m)UPD(am[i],x);
	DFS(1,0);
	sort(Q+1,Q+tmp+1,cmp);
	Q[0].pos=Q[1].pos-1;
	top=0;
	rep(i,1,tmp){
		if(tag[Q[i].pos]||Q[i].pos==Q[i-1].pos){
			hav[++top]=Q[i].lef-dis[Q[i].pos];
			continue;
		}
		if(Q[i].lef<=dis[Q[i].pos]*2)tag[Q[i].pos]=1;
		else hav[++top]=Q[i].lef-dis[Q[i].pos];
	}
	tmp=0;
	for(int r=lst[1];r;r=nxt[r]){
		if(!tag[edge[r]])ned[++tmp]=dis[edge[r]];
	}
	return CAN();
}

LL SOLVE(){
	LL ans=-1;
	LL l=0,r=INF+1000000000;
	while(l<=r){
		LL mid=(l+r)>>1;
		if(CHK(mid))ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}

int main(){
	READ();
	SEARCH(1,0);
	printf("%lld
",SOLVE());
	return 0;
}

tags

倍增 二分

总结

最重要的还是转换题意吧

还有就是看出二分(。・∀・)ノ゙


啊哈终于咕完了,感觉这一年的题就二分和倍增这么翻来覆去覆去翻来地考二分和倍增真的好么窝什么也没说QuuuuuQ

猴啦完结撒花.jpg

2019.10.30

原文地址:https://www.cnblogs.com/SCL123/p/11765024.html