noip 2018 模拟赛16

T 1 T_1 T1——gxc(3830)

Description:

有一个长度为 n n n的序列 A A A,假如有将该序列划分成 m m m个区间,满足每个区间排序后序列 A A A是有序的,求最大的 m m m
n ≤ 200000 , 1 ≤ A i ≤ 1 0 9 n\le 200000,1\le A_i\le 10^9 n200000,1Ai109

Solution:

  • 这题有许多解法,有 Θ ( n ) \Theta(n) Θ(n)的什么前缀最小值,后缀最大值,也可以是 Θ ( 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 chkmax(T &x,T y){return xinline bool chkmin(T &x,T y){return x>y?x=y,1:0;}

const int N=200005;

int n,m;
int A[N],B[N],last[N];

int main(){
//	freopen("gxc.in","r",stdin);
//	freopen("gxc.out","w",stdout);
	scanf("%d",&n);
	REP(i,1,n) scanf("%d",&A[i]),B[i]=A[i];
	sort(B+1,B+n+1);
	m=unique(B+1,B+n+1)-B;
	REP(i,1,n){
		A[i]=lower_bound(B+1,B+m+1,A[i])-B-1;
		last[A[i]]=i;
	}
	int ans=0;
	int j=1,k=0,Mx=0;
	REP(i,1,n){
		chkmax(Mx,A[i]);
		while(k=j) ++ans;
	}
	printf("%d\n",ans);
	return 0;
}

T 2 T_2 T2——story(1780)

Description:

有一个长度为 n n n的序列 A A A,且 A i ∈ [ 1 , K ] A_i \in[1,K] Ai[1,K]
现在有m个操作,有2种操作:
1 x x x v v v表示将位置 A x A_x Ax改为 v v v
2 输出最短的区间长度,满足该区间包含 [ 1 , K ] [1,K] [1,K]的数。
n , m ≤ 50000 , K ≤ 30 n,m\le 50000,K\le 30 n,m50000,K30

Solution:

  • 对于 Θ ( n m ) \Theta(nm) Θ(nm)的做法,是用单调性来维护。
  • 而发现 K K K的范围比较小,那么不难猜到正解的复杂度是 Θ ( m K log ⁡ n ) \Theta(mK\log n) Θ(mKlogn)
  • 因为带修改且实时询问的数据结构,我们很容易会想到用线段树。
  • 但是呢,这个合并不是直接合并的。
  • 对于每种数,我们需要记录左区间的最后一次出现的序列的位置,以及右区间的第一次的出现的序列的位置。
  • 这样,再类似于 Θ ( n m ) \Theta(nm) Θ(nm)的单调性即可合并。

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 chkmax(T &x,T y){return xinline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
templateinline void rd(T&x){
	char c;x=0;
	while((c=getchar())<48);
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
}

const int N=50002,M=32,INF=0x3f3f3f3f;

int n,m,q;
int A[N];
int cnt[M];

struct p20{
	void solve(){
		while(q--){
			int op,x,v;scanf("%d",&op);
			if(op==1){
				scanf("%d%d",&x,&v);
				A[x]=v;
			}
			else {
				int ans=-1;
				REP(len,m,n){
					REP(l,1,n-len+1){
						int r=l+len-1;
						memset(cnt,0,sizeof cnt);
						REP(i,l,r) cnt[A[i]]++;
						bool flag=1;
						REP(i,1,m) if(!cnt[i]) {flag=0;break;}
						if(flag) {
							ans=len;
							goto loop; 
						}
					}
				}
				loop:;
				printf("%d\n",ans);
			}
		}
	}
}p1;

struct p50{
	
	bool check(){
		REP(i,1,m) if(!cnt[i])return 0;
		return 1;
	}
	
	int work(){
		int ans=INF;
		memset(cnt,0,sizeof cnt);
		int R=0,L=1;
		REP(i,1,n){
			R++;
			cnt[A[R]]++;
			while(L1)cnt[A[L]]--,L++;
			if(check()) chkmin(ans,R-L+1);
		}
		return ans==INF?-1:ans;
	}
	
	void solve(){
		
		while(q--){
			int op,x,v;
			scanf("%d",&op);
			if(op==1){
				scanf("%d%d",&x,&v);
				A[x]=v;
			}
			else printf("%d\n",work());
		}
	}
}p2;

int mark1[M],mark2[M];
struct p100{
	
	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 len,num;
			int mn[M],mx[M];
		}tree[N<<2];
		
		void Up(node &P,node L,node R){
			int sz1=0,sz2=0;
			SREP(i,0,L.num){
				P.mn[sz1++]=L.mn[i];
				mark1[A[L.mn[i]]]=1;
			}
			SREP(i,0,R.num){
				P.mx[sz2++]=R.mx[i];
				mark2[A[R.mx[i]]]=1;
				
				if(mark1[A[R.mn[i]]])continue;
				P.mn[sz1++]=R.mn[i];
			}
			SREP(i,0,L.num){
				if(mark2[A[L.mx[i]]])continue;
				P.mx[sz2++]=L.mx[i];
			}
			
			P.num=sz1;
			chkmin(P.len=L.len,R.len);
			
			memset(mark1,0,sizeof mark1);
			memset(mark2,0,sizeof mark2);
			
			if(sz1>1;
			build(lson),build(rson);
			Up(family); 
		}
		
		void update(int x,int p,int v){
			if(tree[p].L==tree[p].R){
				A[x]=v;
				return;
			}
			int mid=(tree[p].L+tree[p].R)>>1;
			if(x<=mid)update(x,p<<1,v);
			else update(x,p<<1|1,v);
			Up(family);
		}
	}T;
	
	void solve(){
		T.build(1,n,1);
		while(q--){
			int op,x,v;
			rd(op);
			if(op==1) rd(x),rd(v),T.update(x,1,v);
			else printf("%d\n",T.tree[1].len==INF?-1:T.tree[1].len);
		}
	}
}p3;

int main(){
//	freopen("story.in","r",stdin);
//	freopen("story.out","w",stdout);
	rd(n),rd(m),rd(q);
	REP(i,1,n) rd(A[i]);
	
	if(n<=300 and q<=300) p1.solve();
	else if(n<=5000 and q<=5000) p2.solve();
	else p3.solve();
	
	return 0;
}

T 3 T_3 T3——farm(3060)

Description:

一棵树,有 q q q个询问,每次询问以 x x x为中心,建 y y y条河,满足河一定与 x x x连通,求每次询问的最大价值(河覆盖的路径长度)。
n , q ≤ 1 0 5 n,q\le 10^5 n,q105

Solution:

  • 首先,我们可以得到一个定理:
  • 对于一个无根树,如果它有 2 k 2k 2k个叶子节点,我们只用 k k k条路径就足以将其覆盖。所以我们就可以把问题变为:在无根树中找 2 y 2y 2y个叶子节点,是其形成一个包含点x的边权最大连通块 S S S
  • 也就是说我们以 x x x为根节点,找 2 y 2y 2y个叶子节点,使它们到形成的 S S S最大。
  • 而模拟发现,我们把这些叶子节点产生的贡献 v a l val val,选出最大的 2 y − 1 2y-1 2y1个即为答案。
  • 但是对于每个询问的 x x x都是不同的。
  • 然后,我们可以发现对于每次询问, S S S中一定会至少包含此树直径中的两端点之一。
  • 所以我们可以分别以直径两端点为根节点的树中找最大的 2 y − 1 2y−1 2y1个叶子节点的 v a l val val之和。
  • 但是这样就又有一个问题了,那就是这样写会导致 x x x可能不在 S S S之中。
  • 但这并不碍事,我们可以将其中一条最小的删掉,来连向 x x x

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 chkmax(T &x,T y){return xinline bool chkmin(T &x,T y){return x>y?x=y,1:0;} 

const int N=1e5+2,S=20; 

int n,q;

int qwq,head[N];
struct edge{
	int to,nxt;
	int w;
}E[N<<1];
void addedge(int x,int y,int 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 Mx,Id;

void dfs(int x,int f,int dis){
	if(chkmax(Mx,dis)) Id=x;
	EREP(x){
		int y=E[i].to;
		if(y==f)continue;
		dfs(y,x,dis+E[i].w);
	}
}

int val_cmp[N];
bool cmp(int x,int y){return val_cmp[x]>val_cmp[y];}

struct node{
	int rt;
	int far[N],dis[N];
	int fa[N][S];
	int cnt,leaf[N],val[N],sum[N],id[N];
	
	void dfs1(int x,int f){
		fa[x][0]=f;
		bool child=0;
		EREP(x){
			int y=E[i].to;
			if(y==f)continue;
			child=1;
			dis[y]=dis[x]+E[i].w;
			dfs1(y,x);
			if(dis[far[x]]=cnt) return sum[cnt];
		if(y>=id[far[x]]) return sum[y];
		
		int t=far[x];
		
		DREP(i,S-1,0) if(fa[x][i] and id[far[fa[x][i]]]>y) x=fa[x][i];
		x=fa[x][0];
		
		return sum[y]+dis[t]-dis[x]-min(dis[far[x]]-dis[x],val[leaf[y]]);
	}
	
}A,B;

void Init(){
	Mx=0;
	dfs(1,0,0);
	A.rt=Id;
	Mx=0;
	dfs(Id,0,0);
	B.rt=Id;
	A.init();
	B.init();
}
 
int main(){
//	freopen("farm.in","r",stdin);
//	freopen("farm.out","w",stdout);
	scanf("%d%d",&n,&q);
	memset(head,-1,sizeof head);
	SREP(i,1,n){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		addedge(a,b,c);
		addedge(b,a,c);
	}
	
	Init();
	
	while(q--){
		int x,y;
		scanf("%d%d",&x,&y);
		y=y*2-1;
		int ans=0;
		chkmax(ans,max(A.solve(x,y),B.solve(x,y)));
		printf("%d\n",ans);
	}
	return 0;
}

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