1. 程式人生 > >noip 2018 模擬賽16

noip 2018 模擬賽16

T1T_1——gxc(3830)

Description:

有一個長度為nn的序列AA,假如有將該序列劃分成mm個區間,滿足每個區間排序後序列AA是有序的,求最大的mmn200000,1Ai109n\le 200000,1\le A_i\le 10^9

Solution:

  • 這題有許多解法,有Θ(n)\Theta(n)的什麼字首最小值,字尾最大值,也可以是Θ(nlogn)\Theta(n\log n)排序,離散。
  • 總之,這類題還是需要考試時模擬發現一定的規律或結論,才能進一步的解題。

Code:

#include<bits/stdc++.h>
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 DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;} 
template<class T>inline 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<Mx)chkmax(j,last[k]),++k;
		if(i>=j) ++ans;
	}
	printf("%d\n",ans);
	return 0;
}

T2T_2——story(1780)

Description:

有一個長度為nn的序列AA,且Ai[1,K]A_i \in[1,K]。 現在有m個操作,有2種操作: 1 xx vv表示將位置AxA_x改為vv; 2 輸出最短的區間長度,滿足該區間包含[1,K][1,K]的數。 n,m50000,K30n,m\le 50000,K\le 30

Solution:

  • 對於Θ(nm)\Theta(nm)的做法,是用單調性來維護。
  • 而發現KK的範圍比較小,那麼不難猜到正解的複雜度是Θ(mKlogn)\Theta(mK\log n)
  • 因為帶修改且實時詢問的資料結構,我們很容易會想到用線段樹。
  • 但是呢,這個合併不是直接合並的。
  • 對於每種數,我們需要記錄左區間的最後一次出現的序列的位置,以及右區間的第一次的出現的序列的位置。
  • 這樣,再類似於Θ(nm)\Theta(nm)的單調性即可合併。

Code:

#include<bits/stdc++.h>
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 DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;} 
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
template<class T>inline 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(L<R and cnt[A[L]]>1)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<m){
				P.len=INF;
				return;
			}
			
			int can=L.num,id=0;
			
			SREP(i,0,L.num) mark1[A[L.mx[i]]]++;
			DREP(i,L.num-1,0){
				while(can<m and id<R.num){
					if(!mark1[A[R.mn[id]]])can++;
					mark1[A[R.mn[id]]]++;
					id++;
				}
				if(can<m)break;
				if(id) chkmin(P.len,R.mn[id-1]-L.mx[i]+1);
				mark1[A[L.mx[i]]]--;
				if(!mark1[A[L.mx[i]]]) can--;
			}
			memset(mark1,0,sizeof mark1);
		}
		
		void build(int L,int R,int p){
			tree[p].L=L,tree[p].R=R;
			tree[p].len=INF;
			if(L==R){
				if(m==1)tree[p].len=1;
				tree[p].mn[0]=L;
				tree[p].mx[0]=L;
				tree[p].num=1;
				return;
			}
			int mid=(L+R)>>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;
}

T3T_3——farm(3060)

Description:

一棵樹,有qq個詢問,每次詢問以xx為中心,建yy條河,滿足河一定與xx連通,求每次詢問的最大價值(河覆蓋的路徑長度)。 n,q105n,q\le 10^5

Solution:

  • 首先,我們可以得到一個定理:
  • 對於一個無根樹,如果它有2k2k個葉子節點,我們只用kk條路徑就足以將其覆蓋。所以我們就可以把問題變為:在無根樹中找2y2y個葉子節點,是其形成一個包含點x的邊權最大連通塊SS
  • 也就是說我們以xx為根節點,找2y2y個葉子節點,使它們到形成的SS最大。
  • 而模擬發現,我們把這些葉子節點產生的貢獻valval,選出最大的2y12y-1個即為答案。
  • 但是對於每個詢問的xx都是不同的。
  • 然後,我們可以發現對於每次詢問,SS中一定會至少包含此樹直徑中的兩端點之一。
  • 所以我們可以分別以直徑兩端點為根節點的樹中找最大的2y12y−1個葉子節點的valval之和。
  • 但是這樣就又有一個問題了,那就是這樣寫會導致xx可能不在SS之中。
  • 但這並不礙事,我們可以將其中一條最小的刪掉,來連向xx

Code:

#include<bits/stdc++.h>
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 DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;} 
template<class T>inline 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]]<dis[far[y]]) far[x]=far[y];
		}
		if(!child)leaf[++cnt]=x,far[x]=x;
	}
	
	void dfs2(int x,int v){
		val[x]=v;
		EREP(x){
			int y=E[i].to;
			if(y==fa[x][0])continue;
			if(far[x]==far[y]) dfs2(y,v+E[i].w);
			else dfs2(y,E[i].w);
		}
	}
	
	void init(){
		dfs1(rt,0);
		SREP(j,1,S) REP(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
		
		dfs2(rt,0);
		memcpy(val_cmp,val,sizeof val_cmp);
		sort(leaf+1,leaf+1+cnt,cmp);
		REP(i,1,cnt) id[leaf[i]]=i,sum[i]=sum[i-1]+val[leaf[i]];
	}
	
	int solve(int x,int y){
		if(y>=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;
}