2021.8.16考试总结[NOIP模拟41]
T1 你相信引力吗
肯定是单调栈维护。但存在重复值,还是个环,不好搞。
发现取区间时不会越过最大值,因此以最大值为断点将环断为序列。在栈里维护当前栈中有多少个与当前元素相等的元素,小分类讨论一下。
最后作为最大值的断点可以与栈中剩下的每个元素组成点对。
$code:$
1 #include<bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 const int NN=5e6+5; 5 int n,m,top,mx,h[NN<<1],stk[NN<<1],cnt[NN<<1]; 6 LL ans; 7 inline int read(){ 8 int x=0,f=1; char ch=getchar(); 9 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 10 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 11 return x*f; 12 } 13 inline void write(LL x,char sp){ 14 char ch[20]; int len=0; 15 if(x<0){ putchar('-'); x=~x+1; } 16 do{ ch[len++]=x%10+(1<<5)+(1<<4); x/=10; }while(x); 17 for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp); 18 } 19 signed main(){ 20 n=read(); 21 for(int i=1;i<=n;i++){ 22 h[i+n]=h[i]=read(); 23 if(h[i]>h[mx]) mx=i; 24 } 25 for(int i=mx;i<mx+n;i++){ 26 while(top&&stk[top]<h[i]) --top, ++ans; 27 if(stk[top]>h[i]) ++ans; 28 else ans+=cnt[top]+(h[i]!=h[mx]); 29 stk[++top]=h[i]; 30 cnt[top]=(stk[top-1]==h[i])?(cnt[top-1]+1):1; 31 } 32 while(top>2){ 33 if(stk[top]==stk[2]) break; 34 --top; ++ans; 35 } 36 write(ans,'\n'); 37 return 0; 38 }
T2 marshland
范围$50$,棋盘,带限制,考虑网络流。
黑白染色,将危险点看作黑点。发现每次覆盖都将占据黑点左右中一个白点,上下中一个白点。让奇偶行白点分列二分图两边即可。
在二分图两列点间插黑点,把黑点拆成入点和出点,中间连费用为危险值的边。跑“最大费用可行流”即可。
$code:$
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int NN=1e6+10,inf=2e9; 4 int to[NN<<1],nex[NN<<1],head[NN],c[NN<<1],w[NN<<1],idx=1; 5 int s,t,incf[NN],pre[NN],dis[NN],flow,value,ans; 6 int n,m,k,sum,val[55][55]; 7 bool stp[55][55],vis[NN]; 8 inline int id(int x,int y){ return (x-1)*n+y; } 9 inline void link(int a,int b,int x,int y){ to[++idx]=b; nex[idx]=head[a]; head[a]=idx; c[idx]=x; w[idx]=y; } 10 inline void add(int a,int b,int x,int y){ link(a,b,x,y); link(b,a,0,-y); } 11 inline int read(){ 12 int x=0,f=1; char ch=getchar(); 13 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 14 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 15 return x*f; 16 } 17 inline void write(int x,char sp){ 18 char ch[20]; int len=0; 19 if(x<0){ putchar('-'); x=~x+1; } 20 do{ ch[len++]=x%10+(1<<5)+(1<<4); x/=10; }while(x); 21 for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp); 22 } 23 inline bool SPFA(){ 24 queue<int>q; 25 memset(dis,-0x3f,sizeof(dis)); 26 memset(vis,0,sizeof(vis)); 27 q.push(s); dis[s]=0; incf[s]=inf; vis[s]=1; 28 while(!q.empty()){ 29 int x=q.front(); q.pop(); vis[x]=0; 30 for(int i=head[x];i;i=nex[i]){ 31 if(!c[i]) continue; 32 int v=to[i]; 33 if(dis[v]<dis[x]+w[i]){ 34 dis[v]=dis[x]+w[i]; 35 incf[v]=min(incf[x],c[i]); 36 pre[v]=i; 37 if(!vis[v]) vis[v]=1, q.push(v); 38 } 39 } 40 } 41 return dis[t]>0; 42 } 43 void MCMF(){ 44 while(SPFA()){ 45 int i,x=t; 46 if(flow+incf[t]>m) break; 47 flow+=incf[t]; 48 value=max(value,value+dis[t]*incf[t]); 49 while(x!=s){ 50 i=pre[x]; 51 c[i]-=incf[t]; 52 c[i^1]+=incf[t]; 53 x=to[i^1]; 54 } 55 } 56 } 57 signed main(){ 58 n=read(); m=read(); k=read(); s=(n*n<<1)+1; t=s+1; idx=1; 59 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) val[i][j]=read(), sum+=val[i][j]; 60 for(int i=1;i<=k;i++) stp[read()][read()]=1; 61 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ 62 if(stp[i][j]) continue; 63 if((i+j)&1) add(id(i,j),id(i,j)+n*n,1,val[i][j]); 64 else if(i&1){ 65 add(s,id(i,j),1,0); 66 if(i>1) add(id(i,j),id(i-1,j),1,0); 67 if(i<n) add(id(i,j),id(i+1,j),1,0); 68 if(j>1) add(id(i,j),id(i,j-1),1,0); 69 if(j<n) add(id(i,j),id(i,j+1),1,0); 70 } else{ 71 add(id(i,j),t,1,0); 72 if(i>1) add(id(i-1,j)+n*n,id(i,j),1,0); 73 if(i<n) add(id(i+1,j)+n*n,id(i,j),1,0); 74 if(j>1) add(id(i,j-1)+n*n,id(i,j),1,0); 75 if(j<n) add(id(i,j+1)+n*n,id(i,j),1,0); 76 } 77 } 78 ans=sum; MCMF(); 79 write(ans-value,'\n'); 80 return 0; 81 }
T3 party?
树形结构,每个人走到所有人的$LCA$。
暴力可以网络流,但我太laji。。(为啥联赛会一场两道网络流???
构造二分图,左边是人,右边是特产,特产连边容量为$1$,二分人与源点连边的容量即为答案。
如果把连边容量为$mid$改为将每个人拆为$mid$个点,问题就转化为求二分图完美匹配。于是有没听说过的霍尔定理:
一个二分图存在完美匹配,当且仅当左部点$x$中任意$k$个点都与右部点$y$中至少$k$个点邻接。
于是可以不用二分。枚举人的所有子集,用它们对应特产数除以人数,取$min$后即为每人最多能带的特产数。
特产可以用$bitset$加树剖维护。维护一个点到它$top$路径上的特产集合可以在树剖时减少一个$log$。
$code:$
1 #include<bits/stdc++.h> 2 #define bst bitset<1024> 3 using namespace std; 4 const int NN=3e5+5; 5 int n,c,m,q,to[NN<<1],nex[NN<<1],head[NN],idx,pla[6],a[NN],ans; 6 int dep[NN],son[NN],siz[NN],top[NN],dfn[NN],fa[NN],id[NN],cnt; 7 bst b[NN]; 8 inline int read(){ 9 int x=0,f=1; char ch=getchar(); 10 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 11 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 12 return x*f; 13 } 14 inline void write(int x,char sp){ 15 char ch[20]; int len=0; 16 if(x<0){ putchar('-'); x=~x+1; } 17 do{ ch[len++]=x%10+(1<<5)+(1<<4); x/=10; }while(x); 18 for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp); 19 } 20 inline void add(int a,int b){ 21 to[++idx]=b; nex[idx]=head[a]; head[a]=idx; 22 to[++idx]=a; nex[idx]=head[b]; head[b]=idx; 23 } 24 struct segment_tree{ 25 #define ld rt<<1 26 #define rd (rt<<1)|1 27 bst bs[NN<<2]; 28 void pushup(int rt){ 29 bs[rt]=bs[ld]|bs[rd]; 30 } 31 void build(int rt,int l,int r){ 32 if(l==r){ bs[rt][a[id[l]]]=1; return; } 33 int mid=l+r>>1; 34 build(ld,l,mid); 35 build(rd,mid+1,r); 36 pushup(rt); 37 } 38 bst query(int rt,int l,int r,int opl,int opr){ 39 if(l>=opl&&r<=opr) return bs[rt]; 40 int mid=l+r>>1; 41 bst tmp; 42 if(opl<=mid) tmp|=query(ld,l,mid,opl,opr); 43 if(opr>mid) tmp|=query(rd,mid+1,r,opl,opr); 44 return tmp; 45 } 46 }s; 47 void dfs1(int s,int f){ 48 fa[s]=f; dep[s]=dep[f]+1; siz[s]=1; 49 for(int i=head[s];i;i=nex[i]){ 50 int v=to[i]; 51 if(v==f) continue; 52 dfs1(v,s); 53 siz[s]+=siz[v]; 54 if(siz[son[s]]<siz[v]) son[s]=v; 55 } 56 } 57 void dfs2(int s,int t){ 58 dfn[s]=++cnt; id[cnt]=s; top[s]=t; 59 if(s!=t) b[s]=b[fa[s]]; b[s].set(a[s]); 60 if(!son[s]) return; 61 dfs2(son[s],t); 62 for(int i=head[s];i;i=nex[i]){ 63 int v=to[i]; 64 if(v!=fa[s]&&v!=son[s]) dfs2(v,v); 65 } 66 } 67 bst S(int x,int lca){ 68 bst tmp; 69 while(top[x]!=top[lca]) tmp|=b[x], x=fa[top[x]]; 70 tmp|=s.query(1,1,n,dfn[lca],dfn[x]); 71 return tmp; 72 } 73 inline int LCA(int x,int y){ 74 while(top[x]!=top[y]){ 75 if(dep[top[x]]<dep[top[y]]) swap(x,y); 76 x=fa[top[x]]; 77 } 78 return dep[x]<dep[y]?x:y; 79 } 80 signed main(){ 81 n=read(); m=read(); q=read(); 82 for(int i=2;i<=n;i++) add(i,read()); 83 for(int i=1;i<=n;i++) a[i]=read(); 84 dfs1(1,0); dfs2(1,1); s.build(1,1,n); 85 while(q--){ 86 c=read(); ans=INT_MAX; 87 for(int i=1;i<=c;i++) pla[i]=read(); 88 int lca=LCA(pla[1],pla[2]); 89 for(int i=3;i<=c;i++) lca=LCA(lca,pla[i]); 90 bst path[6]; 91 for(int i=1;i<=c;i++) path[i]|=S(pla[i],lca); 92 for(int i=1;i<1<<c;i++){ 93 path[0].reset(); 94 int cntt=0; 95 for(int j=0;j<c;j++) 96 if(i&(1<<j)) path[0]|=path[j+1], cntt++; 97 ans=min(ans,(int)path[0].count()/cntt); 98 } 99 write(ans*c,'\n'); 100 } 101 return 0; 102 }