8.17 模拟赛

省流:100+0+0+0=100pts,我菜完了。

T1

题意:\(q\) 次询问,每次删除一条边,判断图是否连通,强制在线,连通输出 0 否则输出 1。

注意到题目里的强制在线是假的,答案一定是一堆 0 + 一堆 1。

那么只要求出 01 的分界点就好了。

考虑离线下来倒着做,先去重,然后跑并查集就好了,复杂度 \(O(n\alpha(n))\)

code

#include <bits/stdc++.h>
//#define BIG
#define MID
#ifdef BIG
#define int __int128
#endif
#ifdef MID
#define int long long
#endif
using namespace std;
const int N=2e6+5;
int n,m,q,u[N],v[N],a[N],b[N],cnt;
bool vis[N];
namespace DSU{
	int fa[N],siz[N],res;
	inline int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
	inline void merge(int x,int y){
		int fx=getfa(x),fy=getfa(y);
		if(fx==fy)return;res--;
		if(siz[fx]>siz[fy])swap(fx,fy);
		fa[fx]=fy,siz[fy]+=siz[fx];
	}
}using namespace DSU;
inline int read(){
	int ret=0,f=1;
	char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}
inline void write(int x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void writesp(int x){write(x),putchar(' ');}
inline void writeln(int x){write(x),putchar('\n');}
signed main(){
	freopen("braintest.in","r",stdin);
	freopen("braintest.out","w",stdout);
	n=read(),m=read(),q=read();
	for(int i=1;i<=m;i++)u[i]=read(),v[i]=read();
	for(int i=1;i<=q;i++)vis[a[i]=read()]=1;
	iota(fa+1,fa+n+1,1),fill(siz+1,siz+n+1,1),res=n;
	for(int i=1;i<=m;i++)if(!vis[i])merge(u[i],v[i]);
	memset(vis,0,sizeof vis);
	for(int i=1;i<=q;i++)
		if(!vis[a[i]])b[++cnt]=a[i],vis[a[i]]=1;
	int pos=0;
	for(int i=cnt;i>=1;i--){
		if(res==1){pos=i+1;break;}
		merge(u[b[i]],v[b[i]]);
	}
	if(!pos&&res==1)pos=1;
	for(int i=1;i<=q;i++)
		if(b[pos]==a[i]){pos=i;break;}
	for(int i=1;i<pos;i++)writeln(0);
	for(int i=pos;i<=q;i++)writeln(1);
	return 0;
}

T2

题意:给定一棵树,求一个集合 \(S\) 满足 \(|S|=m\)\(S\) 中的点在树上连通,求 \((\sum_{i\in S} a_i) * (\sum_{i\in S} b_i)\)

考虑把 \((\sum a_i,\sum b_i)\) 看作平面直角坐标系上的一个点,令最小值为 \(k\),则取到最小值的点一定在函数 \(y=\frac{k}{x}\) 上,不难发现如果一个点被另一个点偏序,那么一定不优,因此答案一定在这些点的下凸壳上,只要求出下凸壳即可。

考虑分治,先求出 \(\sum a_i\) 最小的点 \(A\),以及 \(\sum b_i\) 最小的点 \(B\),考虑求一个点 \(C\)\(C\) 到直线 \(AB\) 的距离最大,那么 \(C\) 一定会在下凸壳上。

等价于让 \(S_{\Delta} ABC\) 的面积最大,就是让 \(\vec {AB} \times \vec{AC}\) 最小(因为叉积有符号,这里为负)。

开始推式子:\(\vec {AB} \times \vec{AC} = (x_2-x_1,y_2-y_1) \times (x_3-x_1,y_3-y_1)=(x_2-x_1)(y_3-y_1)-(y_2-y_1)(x_3-x_1)=(y_1-y_2)x_3+(x_2-x_1)y_3-x_2y_1+x_1y_2\)

后面一块是常数,只要求前面的最小值,不难发现树形背包即可,时间复杂度 \(O(能过)\)

赛时不会,类似原题 P5540,黑题吓哭了。

code

#include <bits/stdc++.h>
#pragma GCC optimize(2)
//#define BIG
#define MID
#ifdef BIG
#define int __int128
#endif
#ifdef MID
#define int long long
#endif
using namespace std;
const int N=2335,INF=1e18;
int n,m,x[N],y[N],w[N],siz[N],ans=INF;
vector<int> G[N];
struct Vector{
	int w,x,y;
	inline bool operator <(const Vector &B)const{return w<B.w;}
}dp[N][N],tmp[N];
inline Vector operator +(const Vector &A,const Vector &B){return {A.w+B.w,A.x+B.x,A.y+B.y};}
inline Vector operator -(const Vector &A,const Vector &B){return {A.w-B.w,A.x-B.x,A.y-B.y};}
inline int operator *(const Vector &A,const Vector &B){return A.x*B.y-A.y*B.x;}
inline int read(){
	int ret=0,f=1;
	char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}
inline void write(int x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void writesp(int x){write(x),putchar(' ');}
inline void writeln(int x){write(x),putchar('\n');}
inline void DFS(int u,int f){
	siz[u]=1;
	for(int i=0;i<=m;i++)dp[u][i]={INF,0,0};
	dp[u][1]={w[u],x[u],y[u]};
	for(auto v:G[u])if(v!=f){
		DFS(v,u);
		for(int i=0;i<=min(siz[u]+siz[v],m);i++)tmp[i]={INF,0,0};
		for(int i=0;i<=min(siz[u],m);i++)
			for(int j=0;j<=min(siz[v],m);j++)tmp[i+j]=min(tmp[i+j],dp[u][i]+dp[v][j]);
		siz[u]+=siz[v];for(int i=0;i<=min(siz[u],m);i++)dp[u][i]=min(dp[u][i],tmp[i]);
	}
}
inline Vector calc(int a,int b){
	for(int i=1;i<=n;i++)w[i]=a*x[i]+b*y[i];
	DFS(1,0);Vector res={INF,0,0};
	for(int i=1;i<=n;i++)res=min(res,dp[i][m]);
	return res;
}
inline void solve(Vector A, Vector B){
	if(A.x*B.y>=ans)return;
	Vector C=calc(A.y-B.y,B.x-A.x);
	ans=min(ans,C.x*C.y);
	if((B-A)*(C-A)>=0)return;
	solve(A,C),solve(C,B);
}
signed main(){
	freopen("ecu.in","r",stdin);
	freopen("ecu.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;i++)x[i]=read(),y[i]=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		G[u].push_back(v);
		G[v].push_back(u);
	}
	Vector A=calc(1,0),B=calc(0,1);
	ans=min(A.x*A.y,B.x*B.y);
	solve(A,B);
	writeln(ans);
	return 0;
}

T3

神秘构造题,我显然不会。

T4

神秘推式子题,我显然不会。

posted on 2026-08-17 15:41  Vector_net  阅读(3)  评论(0)    收藏  举报