2022年6月练习

P5024 [NOIP2018 提高组] 保卫王国

一颗树有 \(n\) 个点 \((1 \le n \le 10^5)\) , 带点权 。

给出 \(m\) 个询问 \((1 \le m \le 10^5)\) ,对于每个询问 ,你需要在树上选若干个点,满足如下条件:

  1. 树上每一条边所连的两个节点中有至少一个被选中;
  2. 限制询问相关的两个点必须被选中或不被选中;

要求你回答在满足上述条件的前提下,所选点点权的和的最小值,若无法满足上述条件则输出 \(-1\)

第一眼:要是没有条件 2 ,这就是一个骡的极小点覆盖集,再加上有 \(10^5\) 个询问,所以必然要使用倍增(或树剖)预处理。

考虑先不顾条件 2 ,考虑每个子树的根节点是否选的情况,用深搜做一遍子树点权和的DP;(然后就不晓得做了,果断贺 TJ )

再进行一次深搜,处理出全树点权和减去当前子树点权和(同样也需考虑子树的根节点是否选的情况)

核心:随后处理出 \(ldp[u][g][0/1][0/1]\) ,意为节点 u 与距其 \(2^n\) 的祖先所构成的链与其上的子树的最小权值和(后两个下标为判断链的两端是否被选)

image
(如图,深搜第一次处理蓝色部分,第二次处理绿色部分,随后预处理链的情况,预计最多需 \(2 \log n\) 条小链构成图中红色的长链)

处理后分别解决每个询问,用 \(LCA\) 计算即可。时间复杂度 \(O(n \log n)\)

总结:这样的水体虽然是码农题,但是写一天实在是太过分了,要提高效率

code
#include <bits/stdc++.h>
#define ll long long
#define rgi register int
using namespace std;
const int M=1e5+7;
const int inf=1e11+7;
inline int read(){
	int w=0,r=1;char c=getchar();
	while(!(isdigit(c)||c=='-'))c=getchar();
	if(c=='-')r=-1,c=getchar();
	while(isdigit(c))w=w*10+c-'0',c=getchar();
	return w*r;
}
void gitgud(){
	int ans=0;
	for(int i=1;i<=100;i++)ans++;
}
int n,m,p[M],tim;
int head[M],tot;
struct edge{
	int pre,to;
}line[M*2];
struct qst{
	int u1,s1,u2,s2;
}q[M];
void add(int fr,int tr){
	line[++tot].pre=head[fr];
	head[fr]=tot;
	line[tot].to=tr;
} 
int up[18][M],depp[M];
int dp[2][M];
int ddp[2][M],ldp[18][M][2][2]; 
//up:2^n辈祖先 dp:子树节点选不选费用 ddp:全树减子树节点选不选费用 ldp:链两端(包含链旁的子树)节点选不选费用(题解思路) 
void dfs(int u,int fa){
	dp[1][u]=p[u];
	up[0][u]=fa;
	depp[u]=depp[fa]+1;
	for(int i=head[u];i;i=line[i].pre){
		int kid=line[i].to;
		if(kid==fa)continue;
		dfs(kid,u);
		dp[0][u]+=dp[1][kid];
		dp[1][u]+=min(dp[0][kid],dp[1][kid]);
	}
//	printf("*%d  %d  %d  %d\n",u,fa,dp[0][u],dp[1][u]);
}
void dfs2(int u,int fa){
	for(int i=head[u];i;i=line[i].pre){
		int kid=line[i].to;
		if(kid==fa)continue;
		ddp[0][kid]=ddp[1][u]+dp[1][u]-min(dp[0][kid],dp[1][kid]);
		ddp[1][kid]=min(ddp[0][u]+dp[0][u]-dp[1][kid],ddp[0][kid]);
		dfs2(kid,u);
	}
}
void preLCA(){
	for(int i=2;i<=n;i++){
		ldp[0][i][0][0]=inf;
		ldp[0][i][0][1]=dp[0][up[0][i]]-dp[1][i];
		ldp[0][i][1][0]=dp[1][up[0][i]]-min(dp[0][i],dp[1][i]);
		ldp[0][i][1][1]=ldp[0][i][1][0];
//		printf("***ldp[0][%d][0][0]=%lld ldp[0][%d][0][1]=%lld ldp[0][%d][1][0]=%lld ldp[0][%d][1][1]=%lld\n",i,ldp[0][i][0][0],i,ldp[0][i][0][1],i,ldp[0][i][1][0],i,ldp[0][i][1][1]);
	}
	for(int i=1;i<18;i++){
		for(int j=2;j<=n;j++){
			up[i][j]=up[i-1][up[i-1][j]];
			ldp[i][j][0][0]=min(ldp[i-1][up[i-1][j]][0][1]+ldp[i-1][j][1][0],ldp[i-1][up[i-1][j]][0][0]+ldp[i-1][j][0][0]);
			ldp[i][j][0][1]=min(ldp[i-1][up[i-1][j]][0][1]+ldp[i-1][j][1][1],ldp[i-1][up[i-1][j]][0][0]+ldp[i-1][j][0][1]);
			ldp[i][j][1][0]=min(ldp[i-1][up[i-1][j]][1][1]+ldp[i-1][j][1][0],ldp[i-1][up[i-1][j]][1][0]+ldp[i-1][j][0][0]);
			ldp[i][j][1][1]=min(ldp[i-1][up[i-1][j]][1][1]+ldp[i-1][j][1][1],ldp[i-1][up[i-1][j]][1][0]+ldp[i-1][j][0][1]);
		}
	}
}
ll num1[2],num2[2],sta1[2],sta2[2];
ll LCA(int u1,int u2,int s1,int s2){
	num1[0]=num1[1]=num2[0]=num2[1]=inf;
	sta1[0]=sta1[1]=sta2[0]=sta2[1]=0;
	if(depp[u1]>depp[u2])swap(u1,u2),swap(s1,s2);
	num1[s1]=dp[s1][u1],num2[s2]=dp[s2][u2];
//	printf("st:%d %d %d %d  %lld %lld\n",u1,s1,u2,s2,num1[s1],num2[s2]);
	for(int i=17;i>=0;i--){
		if(depp[up[i][u2]]>=depp[u1]){
			sta2[0]=min(ldp[i][u2][0][0]+num2[0],ldp[i][u2][0][1]+num2[1]);
			sta2[1]=min(ldp[i][u2][1][0]+num2[0],ldp[i][u2][1][1]+num2[1]);
			num2[0]=sta2[0],num2[1]=sta2[1];
			u2=up[i][u2];
		}
	}
	if(u1==u2)return ddp[s1][u1]+num2[s1];
	for(int i=17;i>=0;i--)
		if(up[i][u2]!=up[i][u1]){
			sta1[0]=min(ldp[i][u1][0][0]+num1[0],ldp[i][u1][0][1]+num1[1]);
			sta1[1]=min(ldp[i][u1][1][0]+num1[0],ldp[i][u1][1][1]+num1[1]);
			num1[0]=sta1[0],num1[1]=sta1[1];
			sta2[0]=min(ldp[i][u2][0][0]+num2[0],ldp[i][u2][0][1]+num2[1]);
			sta2[1]=min(ldp[i][u2][1][0]+num2[0],ldp[i][u2][1][1]+num2[1]);
			num2[0]=sta2[0],num2[1]=sta2[1];
			u1=up[i][u1],u2=up[i][u2];
		}
//	printf("st:%d %d %d %d  %lld %lld\n",u1,s1,u2,s2,num1[s1],num2[s2]);
	int lca=up[0][u1];
	int an1=dp[0][lca]-dp[1][u1]-dp[1][u2]+num1[1]+num2[1]+ddp[0][lca];
	int an2=dp[1][lca]-min(dp[0][u1],dp[1][u1])+min(num1[0],num1[1])-min(dp[0][u2],dp[1][u2])+min(num2[0],num2[1])+ddp[1][lca];
//	printf("ed:%d %d %d %lld %lld    %lld %lld %lld %lld\n",lca,u1,u2,an1,an2,num1[0],num1[1],num2[0],num2[1]);
	return max(an1,an2);
}
int main(){
//  freopen("text.in","r",stdin);
//	freopen("text.out","w",stdout);
    n=read(),m=read(),read();
    for(int i=1;i<=n;i++)p[i]=read();
    for(int i=1;i<n;i++){
    	int fr=read(),tr=read();
    	add(fr,tr),add(tr,fr);
	}
	for(int i=1;i<=m;i++){
		q[i]=(qst){read(),read(),read(),read()};
	}
	dfs(1,0);
	dfs2(1,0);
	preLCA();
	for(int i=1;i<=m;i++){
		tim=i;
		int anss=LCA(q[i].u1,q[i].u2,q[i].s1,q[i].s2);
		printf("%d\n",(anss>=inf)?-1:anss);
	}
	return 0;
}
/*
5 3 C3 
2 4 1 3 9 
1 5 
5 2 
5 3 
3 4 
1 0 3 0 
2 1 3 1 
1 0 5 0
别想直接复制
*/


「SNOI2017」炸弹

按照这篇博客中的思路,复杂度为 \(O(n)\)

code
#include <bits/stdc++.h>
#define ll long long
#define rgi register int
using namespace std;
const int M=5e5+7,inf=1e9+7,modd=1e9+7;
inline ll read(){
	ll w=0,r=1;char c=getchar();
	while(!(isdigit(c)||c=='-'))c=getchar();
	if(c=='-')r=-1,c=getchar();
	while(isdigit(c))w=w*10+c-'0',c=getchar();
	return w*r;
}
void gitgud(){
	int ans=0;
	for(int i=1;i<=100;i++)ans++;
}
ll n,x[M],r[M],bl[M],br[M];
int main(){
    gitgud();
    n=read();
    for(int i=1;i<=n;i++){
    	x[i]=read(),r[i]=read();
    	bl[i]=br[i]=i;
	}
    for(int i=2;i<=n;i++)while(bl[i]!=1&&x[i]-x[bl[i]-1]<=r[i]){
    	r[i]=max(r[i],x[bl[i]-1]+r[bl[i]-1]-x[i]);
    	bl[i]=bl[bl[i]-1];
	}
	for(int i=n-1;i;i--)while(br[i]!=n&&x[br[i]+1]-x[i]<=r[i]){
		bl[i]=min(bl[i],bl[br[i]+1]);
    	br[i]=br[br[i]+1];
	}
//	for(int i=1;i<=n;i++)printf("**%lld %lld\n",bl[i],br[i]);
	ll ans=0;
	for(int i=1;i<=n;i++)ans=(ans+(ll)i*(br[i]-bl[i]+1)%modd)%modd;
	printf("%lld\n",ans);
	return 0;
}
/*
4
1 1
5 1
6 5
15 15
*/


P6794 [SNOI2020] 水池

同上。
code


P7113 [NOIP2020] 排水系统

DAG->拓扑序板子题
需要用__int128,否则只能得90pts
我__int128的输出写挂了,找了两个小时错误

code
#include <bits/stdc++.h>
#define ll long long
#define rgi register int
using namespace std;
const int M=2e5+7,inf=1e9+7,modd=1e9+7;
inline int read(){
	int w=0,r=1;char c=getchar();
	while(!(isdigit(c)||c=='-'))c=getchar();
	if(c=='-')r=-1,c=getchar();
	while(isdigit(c))w=w*10+c-'0',c=getchar();
	return w*r;
}
void gitgud(){
	int ans=0;
	for(int i=1;i<=100;i++)ans++;
}
int n,q,w[M],tot,rt[M*70];
struct node{
	int ls,rs,lzy;
	int h,bl,br;//bl,br:区间内左板最大、右板最大 
};
struct sgt{
	node d[M*70];
	void pushup(int u){
		d[u].bl=max(d[d[u].ls].bl,d[d[u].rs].bl);
		d[u].br=max(d[d[u].ls].br,d[d[u].rs].br);
	}
	int abuild(int u){
		int ii=++tot;
		d[ii]=d[u];
		return ii;
	}
	void pushdown(int u){
		if(d[u].lzy){
			d[u].ls=abuild(d[u].ls),d[u].rs=abuild(d[u].rs);
			d[d[u].ls].lzy=d[d[u].rs].lzy=d[u].lzy;
			if(d[u].lzy==1){
				d[d[u].ls].h=d[d[u].rs].h=d[u].h;
			}else if(d[u].lzy==2){
				d[d[u].ls].h=max(d[u].h,d[d[u].rs].bl),d[d[u].rs].h=d[u].h;
			}else{
				d[d[u].ls].h=d[u].h,d[d[u].rs].h=max(d[u].h,d[d[u].ls].br);
			}
			d[u].lzy=0;
		}
	}
	void build(int &u,int l,int r){
		u=++tot;
		if(l==r){
			d[u].bl=w[l-1],d[u].br=w[l];
			return;
		}
		int mid=(l+r)>>1;
		build(d[u].ls,l,mid),build(d[u].rs,mid+1,r);
		pushup(u);
	}
	int query_h(int u,int l,int r,int x){
		if(l==r)return d[u].h;
		int mid=(l+r)>>1;
		pushdown(u);
		if(x<=mid)return query_h(d[u].ls,l,mid,x);
		else return query_h(d[u].rs,mid+1,r,x);
	}
	int query_l(int u,int l,int r,int x,int val){
		if(d[u].bl<val)return -1;
		if(l==r)return l;
		int mid=(l+r)>>1;
		pushdown(u);
		if(x<=mid)return query_l(d[u].ls,l,mid,x,val);
		int an=query_l(d[u].rs,mid+1,r,x,val);
		return an==-1?query_l(d[u].ls,l,mid,x,val):an;
	}
	int query_r(int u,int l,int r,int x,int val){
		if(d[u].br<val)return -1;
		if(l==r)return l;
		int mid=(l+r)>>1;
		pushdown(u);
		if(mid<x)return query_r(d[u].rs,mid+1,r,x,val);
		int an=query_r(d[u].ls,l,mid,x,val);
		return an==-1?query_r(d[u].rs,mid+1,r,x,val):an;
	}
	void pour_rb(int &u,int l,int r,int x,int y,int val){
		u=abuild(u);
		if(x<=l&&r<=y){
			d[u].lzy=1,d[u].h=val;
			return;
		}
		pushdown(u);
		int mid=(l+r)>>1;
		if(x<=mid)pour_rb(d[u].ls,l,mid,x,y,val);
		if(y>mid)pour_rb(d[u].rs,mid+1,r,x,y,val);
//		pushup(u,l,r);
	}
	void change_l(int &u,int l,int r,int x,int y,int &val){
		u=abuild(u);
		if(x<=l&&r<=y){
			d[u].lzy=2,d[u].h=val;
			val=max(val,d[u].bl); 
			return;
		}
		pushdown(u);
		int mid=(l+r)>>1;
		if(y>mid)change_l(d[u].rs,mid+1,r,x,y,val);//左边位置的水位取决于已查左板(将来右板)的高度; 
		if(x<=mid)change_l(d[u].ls,l,mid,x,y,val);
	}
	void change_r(int &u,int l,int r,int x,int y,int &val){
		u=abuild(u);
		if(x<=l&&r<=y){
			d[u].lzy=3,d[u].h=val;
			val=max(val,d[u].br);
			return;
		}
		pushdown(u);
		int mid=(l+r)>>1;
		if(x<=mid)change_r(d[u].ls,l,mid,x,y,val);
		if(y>mid)change_r(d[u].rs,mid+1,r,x,y,val);//右边位置的水位取决于已查右板(将来左板)的高度; 
	}
	void change_b(int &u,int l,int r,int x,int val,int tp){
		u=abuild(u);
		if(l==r){
			tp?(d[u].br=val):(d[u].bl=val);
			return;
		}
		int mid=(l+r)>>1;
		pushdown(u);
		if(x<=mid)change_b(d[u].ls,l,mid,x,val,tp);
		else change_b(d[u].rs,mid+1,r,x,val,tp);
		pushup(u);
	}
}T;
int main(){
    gitgud();
    n=read(),q=read();
    w[0]=w[n]=inf;
    for(int i=1;i<n;i++)w[i]=read();
    T.build(rt[0],1,n);
    for(int i=1;i<=q;i++){
    	int typ=read(),tim=read();
    	rt[i]=rt[tim];
    	if(typ==0){
    		int x=read(),val=read();
    		int hh=T.query_h(rt[i],1,n,x);
    		if(hh<val){
    			int xl=T.query_l(rt[i],1,n,x,val);
    			int xr=T.query_r(rt[i],1,n,x,val);
    			T.pour_rb(rt[i],1,n,xl,xr,val); 
			}
		}else if(typ==1){
    		int x=read(),val=0;
    		int hh=T.query_h(rt[i],1,n,x);
    		int xl=T.query_l(rt[i],1,n,x,hh);
    		int xr=T.query_r(rt[i],1,n,x,hh);
    		val=0;
    		T.change_l(rt[i],1,n,xl,x,val); 
    		val=0;
    		T.change_r(rt[i],1,n,x,xr,val); 
		}else if(typ==2){
    		int x=read(),val=read();
    		T.change_b(rt[i],1,n,x+1,val,0);
    		T.change_b(rt[i],1,n,x,val,1);
		}else {
			int x=read();
			printf("%d\n",T.query_h(rt[tim],1,n,x));
		}
	}
	return 0;
}
/*
4 9
1 3 2
0 0 4 2
3 1 1
0 2 4 3
3 3 1
0 4 4 4
3 5 1
2 6 1 4
1 7 4
3 8 1
*/

P2827 [NOIP2016 提高组] 蚯蚓

pts80:
首先考虑本题最大难点:蚯蚓生长规律。每秒除生成的蚯蚓外的所有蚯蚓长度增长 \(q\)
我们将增长的 \(q\) 的总和记为 \(qt\),将生成的两条蚯蚓各自长度减少 \(qt\) ,这样各条蚯蚓的长度差值不变。
\(qt\) 代入对蚯蚓长度的计算,用一个单调栈维护一下即可 。
pts100:
我们注意到,长蚯蚓分的两条小蚯蚓一定不比短蚯蚓分的两条小蚯蚓短,这是蚯蚓群自带的单调性。
将剪出来的蚯蚓放到两个队列中去,它们会自己形成单调队列。
将答案用单调栈维护一下就行了。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rgi register long long
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ROF(i,a,b) for(int i=a;i>=b;i--)
const int M=2e5+7,inf=1e9+7,modd=998244353;
inline int read(){
	int w=0,r=1;char c=getchar();
	while(!(isdigit(c)||c=='-'))c=getchar();
	if(c=='-')r=-1,c=getchar();
	while(isdigit(c))w=w*10+c-'0',c=getchar();
	return w*r;
}
inline void print(int n){
	if(n<0){
		putchar('-');
		n=-n;
	}
	if(n>9)print(n/10);
	putchar(n%10+'0');
}
int n,m,q,fz,fm,t,qt;
priority_queue<int>ans;
int qy[M],qy1[M*50],qy2[M*50];
int hd[4],tl[4];
void init(){
	n=read(),m=read(),q=read(),fz=read(),fm=read(),t=read();
	for(int i=1;i<=n;i++)qy[i]=read();
}
bool cmp(int aa,int bb){
	return aa>bb;
}
void work(){
	sort(qy+1,qy+n+1,cmp);
	tl[1]=n,tl[2]=tl[3]=0,hd[1]=hd[2]=hd[3]=1;
	for(rgi i=1;i<=m;i++){
		int lg;
		if(hd[1]<=tl[1]&&(hd[2]>tl[2]||qy1[hd[2]]<=qy[hd[1]])&&(hd[3]>tl[3]||qy2[hd[3]]<=qy[hd[1]]))lg=qy[hd[1]++]+qt;
		else if(hd[2]<=tl[2]&&(hd[1]>tl[1]||qy[hd[1]]<=qy1[hd[2]])&&(hd[3]>tl[3]||qy2[hd[3]]<=qy1[hd[2]]))lg=qy1[hd[2]++]+qt;
		else if(hd[3]<=tl[3]&&(hd[2]>tl[2]||qy1[hd[2]]<=qy2[hd[3]])&&(hd[1]>tl[1]||qy[hd[1]]<=qy2[hd[3]]))lg=qy2[hd[3]++]+qt;
		if(i%t==0)print(lg),putchar(' ');
		qt+=q;
		int g=lg*fz/fm;
		int q1=g-qt,q2=lg-g-qt;
//		printf("%lld %lld %lld %lld\n",lg,g,q1,q2);
		qy1[++tl[2]]=q1,qy2[++tl[3]]=q2;
//		printf("result %lld\n",-hd[1]+tl[1]-hd[2]+tl[2]-hd[3]+tl[3]);
	}
	printf("\n");
	for(int i=hd[1];i<=tl[1];i++)ans.push(qy[i]);
	for(int i=hd[2];i<=tl[2];i++)ans.push(qy1[i]);
	for(int i=hd[3];i<=tl[3];i++)ans.push(qy2[i]);
//	printf("*****%d %d %d %d %d %d\n",hd[1],tl[1],hd[2],tl[2],hd[3],tl[3]);
	int tim=0;
	while(!ans.empty()){
		tim++;
		if(tim%t==0)print(ans.top()+qt),putchar(' ');
		ans.pop();
	}
}
signed main(){
//	freopen("text.in","r",stdin);
//	freopen("text.out","w",stdout);
	init();
	work();
	return 0;
}
/*
3 7 1 1 3 1
3 3 2
*/

------------------- ###[P5331 [SNOI2019]通信](https://www.luogu.com.cn/problem/P5331 ) 高级网络流题目。 考虑将每一个哨站建两个点: $S \rightarrow d_i$ ,容量为 1,费用为 0 ; $d_i \rightarrow T$ ,容量为 1,费用为 w ; $d_i \rightarrow d'_j$ ,容量为 1,费用为 $\left| a_i - a_j \right| $ ; $d'_i \rightarrow T$ ,容量为 1 ,费用为 0; 但是建边数为 $n^2$ ,会TLE ,pts80左右(我听说在数据加强之前,这种暴力做法是能过的,感谢cnyz大佬的辛勤付出) 正解是加一个类似归并排序的算法优化建图,使边数变为 $n \sqrt n$ 。 我TLE了半天,一直在疯狂卡常,最后发现是数组开小了 本题启示:导致TLE,CE,RE等的原因有可能是数组空间开小。 [code](https://loj.ac/s/1495291 "code")

P2153 [SDOI2009] 晨跑

标准网络流题目。
将上午通信正解的优化建图减去,再稍微改一下,就做出来了。
code
同时,紫题100祭~

posted @ 2022-06-25 20:00  wcy2006  阅读(32)  评论(0)    收藏  举报