省选模拟32

A. 梦批糼

考虑容斥求,至少选中一次每个点的概率

于是只需要求出有多少个合法的长方体不经过这个点

然后再用总的概率减去每次都选择了不经过的概率再乘上权值

于是枚举长方体的左下角,再枚举长方体的长和高,发现此时宽是单调的

然后再差分一下就能求出经过每个点的长方体个数

我用的暴力跑过去的

B. 等你哈苏德

发现做差的绝对值小于等于 \(1\)

然后考虑每个区间被覆盖的情况,要么是奇数次,要么是偶数次

偶数次的话答案只能为 \(0\)

将黑白色分别看成 \(+1\)\(-1\)

将修改差分一下,于是区间的差分数组也必须为 \(0\)

转化一下,将连接的左右端点建边

再考虑将染色定一下向,黑色为 \(l\)\(r+1\) 连,白色为 \(r+1\)\(l\) 连,没有染色就是一条无向边

于是若区间差分数组为 \(0\) ,也就相当与存在一条欧拉回路

如果都没有被染色,那么就是无向图欧拉回路的判定

有被染色的就考虑混合图欧拉回路的判定,可以写一个网络流

再看被覆盖奇数次的区间,可以额外加一条 \(l,r\) 的无向边

这样就能将 \(+1\)\(-1\) 调整成 \(0\)

Code
#include<bits/stdc++.h>
#define int long long//OVERFLOW !!! MEMORY LIMIT !!!
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,S,T,sum;
int O[60010],tot;
int in[60010],out[60010],num[60010];
int id[30010];
struct data{int l,r,k;}L[30010];
int dis[60010],q[60010],h,t;
int head[60010],HD[60010],ver[600010],to[600010],edge[600010];
inline void add(int x,int y,int z){
	ver[++tot]=y;edge[tot]=z;to[tot]=head[x];head[x]=tot;
	ver[++tot]=x;edge[tot]=0;to[tot]=head[y];head[y]=tot;
}
inline bool bfs(){
	for(int i=1;i<=T;i++) dis[i]=inf;dis[S]=0;q[h=t=1]=S;memcpy(head,HD,sizeof(head));
	while(h<=t){
		int x=q[h++];
		for(int i=head[x];i;i=to[i]) if(edge[i]){
			int y=ver[i];
			if(dis[y]>dis[x]+1){
				dis[y]=dis[x]+1;
				q[++t]=y;
			}
		}
		if(x==T) return true;
	}
	return false;
}
int dfs(int x,int in){
	if(x==T) return in;
	int res=in,go;
	for(int i=head[x];i;head[x]=i=to[i]) if(edge[i]){
		int y=ver[i];
		if(dis[y]!=dis[x]+1) continue;
		go=dfs(y,min(edge[i],res));
		if(go) res-=go,edge[i]-=go,edge[i^1]+=go;
		else dis[y]=inf;
		if(!res) break;
	}
	return in-res;
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("wait.in","r",stdin);
	freopen("wait.out","w",stdout);
	m=read(),n=read();
	for(int i=1;i<=m;i++){
		O[++tot]=L[i].l=read();
		O[++tot]=L[i].r=read()+1;
		L[i].k=read();
	}
	sort(O+1,O+1+tot);n=unique(O+1,O+1+tot)-O-1;S=n+1,T=n+2;tot=1;
	for(int i=1;i<=m;i++){
		L[i].l=lower_bound(O+1,O+1+n,L[i].l)-O;
		L[i].r=lower_bound(O+1,O+1+n,L[i].r)-O;
	}
	for(int i=1;i<=m;i++){
		num[L[i].l]++,num[L[i].r]--;
		if(L[i].k==-1){out[L[i].l]++;in[L[i].r]++;add(L[i].r,L[i].l,1);id[i]=tot;}
		else if(L[i].k==0){out[L[i].l]++;in[L[i].r]++;}
		else{in[L[i].l]++;out[L[i].r]++;}
	}
	for(int i=1;i<=n;i++){
		num[i]+=num[i-1];
		if(abs(num[i])&1){out[i]++;in[i+1]++;add(i+1,i,1);}
	}
	for(int i=1;i<=n;i++){
		if(abs(in[i]-out[i])&1) puts("-1"),exit(0);
		if(in[i]>out[i]) add(S,i,(in[i]-out[i])/2),sum+=(in[i]-out[i])/2;
		else if(out[i]>in[i]) add(i,T,(out[i]-in[i])/2);
	}
	memcpy(HD,head,sizeof(head));while(bfs()) sum-=dfs(S,inf);
	if(sum) puts("-1"),exit(0);
	for(int i=1;i<=m;i++){
		if(L[i].k!=-1) printf("%lld ",L[i].k);
		else printf("%lld ",edge[id[i]]);
	}
	return 0;
}

C. 喜欢最最痛

考虑链接了两个点

走这个边就是相当于可以少走两个点之间的路径一遍

于是用树形 \(dp\) 找到带权的直径就行

发现任意两条路径不能有交,否则无法构造一种方案

一开始想的是,把选择的路径都断开,以后不能再走

发现过不去样例,画图发现有这样一种情况

可以断开已经走过的直径,然后分别去匹配新的端点

这样的话原来的贡献就没了,于是把边权变成负的再跑就对了

不会做 \(ddp\) 于是就写的 \(O(nm)\)

Code
#include<bits/stdc++.h>
#define int long long//OVERFLOW !!! MEMORY LIMIT !!!
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,ans,mx,x,y;
int fa[100010],dep[100010],f[100010],pos[100010];
int head[100010],ver[200010],to[200010],edge[200010],tot=1;
int vis[100010],now;
priority_queue<int>q;
inline void add(int x,int y,int z){ver[++tot]=y;edge[tot]=z;to[tot]=head[x];head[x]=tot;}
void dfs(int x,int fa){
	::fa[x]=fa;dep[x]=dep[fa]+1;f[x]=0,pos[x]=x;vis[x]=now;
	for(int i=head[x];i;i=to[i]) if(edge[i]){
		int y=ver[i];if(y==fa) continue;
		dfs(y,x);f[y]+=edge[i];
		if(f[x]+f[y]>mx) mx=f[x]+f[y],::x=pos[x],::y=pos[y];
		if(f[y]>f[x]) f[x]=f[y],pos[x]=pos[y];
	}
	if(f[x]>mx) mx=f[x],::x=x,::y=pos[x];
}
inline void del(int x){
	for(int i=head[x];i;i=to[i]){
		int y=ver[i];
		if(y==fa[x]) edge[i]=edge[i^1]=-edge[i];
	}
}
inline void upd(int x,int y){
	while(dep[x]>dep[y]){del(x);x=fa[x];}
	while(dep[y]>dep[x]){del(y);y=fa[y];}
	while(x!=y){
		del(x);x=fa[x];
		del(y);y=fa[y];
	}
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("love.in","r",stdin);
	freopen("love.out","w",stdout);
	n=read(),m=read();
	for(int i=1,x,y,z;i<n;i++){
		x=read(),y=read(),z=read();
		add(x,y,z),add(y,x,z);ans+=2*z;
	}
	printf("%lld ",ans);
	for(int i=1,a;i<=m;i++){
		a=read();mx=0,x=0,y=0;now++;
		for(int j=1;j<=n;j++) if(vis[j]!=now) dfs(j,0);
		if(q.size()){
			if(mx>q.top()){if(mx>a){ans-=mx;ans+=a;q.push(a);upd(x,y);}}
			else{if(q.top()>a){ans-=q.top();ans+=a;q.pop();q.push(a);}}
		}else if(mx>a){ans-=mx;ans+=a;q.push(a);upd(x,y);}
		printf("%lld ",ans);
	}
	return 0;
}

赛时正序看题,倒序开题, \(T1\) 会了 \(n^6\) 的做法就先跳了

然后先写的 \(T3\) 的暴力,边想边写大概 \(1h\) 把样例过了

正解应该就是用数据结构去维护这个 \(ddp\)

于是就去 \(T2\) 了,想着写个退火也许能多搞几分,然后就调了一年

结果最后 \(T1\)\(n^6\) 没调出来就裂开了

posted @ 2022-03-15 20:12  Max_QAQ  阅读(82)  评论(0编辑  收藏  举报