noip 2018 模擬賽16
阿新 • • 發佈:2018-12-19
——gxc(3830)
Description:
有一個長度為的序列,假如有將該序列劃分成個區間,滿足每個區間排序後序列是有序的,求最大的。
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) #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; }
——story(1780)
Description:
有一個長度為的序列,且。 現在有m個操作,有2種操作: 1 表示將位置改為; 2 輸出最短的區間長度,滿足該區間包含的數。
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) #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; }
——farm(3060)
Description:
一棵樹,有個詢問,每次詢問以為中心,建條河,滿足河一定與連通,求每次詢問的最大價值(河覆蓋的路徑長度)。
Solution:
- 首先,我們可以得到一個定理:
- 對於一個無根樹,如果它有個葉子節點,我們只用條路徑就足以將其覆蓋。所以我們就可以把問題變為:在無根樹中找個葉子節點,是其形成一個包含點x的邊權最大連通塊。
- 也就是說我們以為根節點,找個葉子節點,使它們到形成的最大。
- 而模擬發現,我們把這些葉子節點產生的貢獻,選出最大的個即為答案。
- 但是對於每個詢問的都是不同的。
- 然後,我們可以發現對於每次詢問,中一定會至少包含此樹直徑中的兩端點之一。
- 所以我們可以分別以直徑兩端點為根節點的樹中找最大的個葉子節點的之和。
- 但是這樣就又有一個問題了,那就是這樣寫會導致可能不在之中。
- 但這並不礙事,我們可以將其中一條最小的刪掉,來連向。
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;
}