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

noip 2018 模擬賽12

T1T_1——gold(3079)

Description:

有一個nmn \cdot m的矩形,對於每一行,可以選擇前k,k[1,n]k,k\in[1,n]個數。 求全部選完後的平均值的最大值。 nm105,Ai,j109n \cdot m\le 10^5,A_{i,j}\le 10^9

Solution:

  • 一看到求平均數、中位數之類的,有極大可能是要二分答案的。
  • 對於check,我們統計出每一行的前kk個數與midmid的差值和的最大值的和,即比較當前mid是否最優。
  • 這樣複雜度為Θ(nmlog(109))\Theta(nm\log(10^9))

Code:

#include<bits/stdc++.h>
using namespace std;
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
#define db double

const db inf=1.0/0.0;
const int N=1e5+2;

int n,m;
vector<int>A[N];

bool check(db x){
	db res=0;
	SREP(i,0,n){
		db mx=-inf,sum=0;
		SREP(j,0,m){
			sum+=A[i][j]-x;
			chkmax(mx,sum);
		}
		res+=mx;
	}
	return res>=0;
}

int main(){
//	freopen("gold.in","r",stdin);
//	freopen("gold.out","w",stdout);
	scanf("%d%d",&n,&m);
	SREP(i,0,n){
		A[i].resize(m);
		SREP(j,0,m) scanf("%d",&A[i][j]);
	}
	db l=0,r=1e9;
	while(r-l>1e-6){
		db mid=1.0*(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%.4lf\n",l);
	return 0;
}

T2T_2——road(3080)

Description:

有兩排城市,每一排的城市編號都為1~n,對於每對編號相同的城市建一條邊。 求最大的城市集合,滿足集合內城市之間的邊都相交。 n105n\le 10^5

Solution:

  • 通過模擬小資料,將第二排的城市編號翻轉,發現,符合的城市集合即為兩排城市的公共子序列。
  • 那麼問題就是求最長公共子序列了。
  • 首先有一個Θ(n2)\Theta(n^2)求最長公共子序列的dpdp,但複雜度顯然不夠。
  • 所以就需要資料結構優化一下,達到Θ(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)
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){
    x=0;char c;
    while((c=getchar())<48);
    do x=(x<<1)+(x<<3)+(c^48);
    while((c=getchar())>47);
}

const int N=100005;

int n;
int A[N],B[N],T[N];

int bit[N];
void add(int x,int v){
	for(;x;x-=(x&-x)) chkmax(bit[x],v);
}
int query(int x){
	int res=0;
	for(;x<N;x+=(x&-x)) chkmax(res,bit[x]);
	return res;
}

int main(){
//	freopen("road.in","r",stdin);
//	freopen("road.out","w",stdout);
	scanf("%d",&n);
	REP(i,1,n) Rd(A[i]);
	REP(i,1,n) Rd(B[i]),T[B[i]]=i;
	int ans=0;
	REP(i,1,n){
		int res=query(T[A[i]]);
		chkmax(ans,res+1);
		add(T[A[i]],res+1);
	}
	printf("%d\n",ans);
	return 0;
}

T3T_3——queue(3081)

Description:

有一棵樹,現在有mm個操作,對於每個操作,有22種: 1.有xx個人從根11往下走(走的策略是該節點的編號最小且沒有人),求最後一人在哪個節點。 2.將節點xx的人帶走,xx以上的節點的人會往下補充,求有多少人移動了。 n,m105n,m\le10^5

Solution:

  • 首先對於一棵樹,我們可以先將其轉化到序列上,即dfsdfs序。
  • 並且,我們先將邊的編號排序,這樣保證最小。
  • 對於操作1,即為從後往前覆蓋序列;
  • 對於操作2,即為修改一條鏈上的資訊,以及求出其標記的鏈的長度。
  • 對於序列的覆蓋以及修改,很容易想到線段樹,而對於求標記的鏈長度,我們可以倍增跳。

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)
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){
    x=0;char c;
    while((c=getchar())<48);
    do x=(x<<1)+(x<<3)+(c^48);
    while((c=getchar())>47);
}

const int S=18,N=100005;

int n,m;
vector<int>E[N];
int Rt[N],sgID[N],tim;
int fa[N][S];
int sum[N<<2];
bool mark[N];
int ans;

#define lson L,mid,p<<1
#define rson mid+1,R,p<<1|1
void update(int L,int R,int p,int&x){
	if(!x or sum[p]==R-L+1) return;
	if(L==R){
		sum[p]=1;
		mark[L]=1;
		if(!--x) ans=L;
		return;
	}
	int mid=(L+R)>>1;
	update(lson,x),update(rson,x);
	sum[p]=sum[p<<1]+sum[p<<1|1];
}

void del(int L,int R,int p,int x){
	--sum[p];
	if(L==R) return;
	int mid=(L+R)>>1;
	if(x<=mid)del(lson,x);
	else del(rson,x);
}

void dfs(int x,int f){
	fa[x][0]=f;
	SREP(i,0,E[x].size()) if(E[x][i]!=f) dfs(E[x][i],x);
	sgID[Rt[x]=++tim]=x;
}

int main(){
//	freopen("queue.in","r",stdin);
//	freopen("queue.out","w",stdout);
	Rd(n),Rd(m);
	SREP(i,1,n){
		int a,b;
		Rd(a),Rd(b);
		E[a].push_back(b);
		E[b].push_back(a);
	}
	
	REP(i,1,n) sort(E[i].begin(),E[i].end());
	dfs(1,0);
	SREP(j,1,S) REP(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
	while(m--){
		int op,x;
		Rd(op),Rd(x);
		if(op==1){
			update(1,n,1,x);
			printf("%d\n",sgID[ans]);
		}
		else{
			ans=0;
			DREP(k,S-1,0) if(mark[Rt[fa[x][k]]]) x=fa[x][k],ans|=1<<k;
			mark[Rt[x]]=0;
			del(1,n,1,Rt[x]);
			printf("%d\n",ans);
		}
	}
	return 0;
}