noip 2018 模擬賽12
阿新 • • 發佈:2018-12-18
——gold(3079)
Description:
有一個的矩形,對於每一行,可以選擇前個數。 求全部選完後的平均值的最大值。
Solution:
- 一看到求平均數、中位數之類的,有極大可能是要二分答案的。
- 對於check,我們統計出每一行的前個數與的差值和的最大值的和,即比較當前mid是否最優。
- 這樣複雜度為
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; }
——road(3080)
Description:
有兩排城市,每一排的城市編號都為1~n,對於每對編號相同的城市建一條邊。 求最大的城市集合,滿足集合內城市之間的邊都相交。
Solution:
- 通過模擬小資料,將第二排的城市編號翻轉,發現,符合的城市集合即為兩排城市的公共子序列。
- 那麼問題就是求最長公共子序列了。
- 首先有一個求最長公共子序列的,但複雜度顯然不夠。
- 所以就需要資料結構優化一下,達到,這裡線段樹或者樹狀陣列都行。
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;
}
——queue(3081)
Description:
有一棵樹,現在有個操作,對於每個操作,有種: 1.有個人從根往下走(走的策略是該節點的編號最小且沒有人),求最後一人在哪個節點。 2.將節點的人帶走,以上的節點的人會往下補充,求有多少人移動了。
Solution:
- 首先對於一棵樹,我們可以先將其轉化到序列上,即序。
- 並且,我們先將邊的編號排序,這樣保證最小。
- 對於操作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;
}