#1 CF1935 Div.2

A

发现翻转前后,字符串开头永远只能为第一个字母或最后一个,根据翻转次数分讨即可。

B

发现分块后的 mex \text{mex} mex 一定是序列本身的 mex \text{mex} mex,显然块与块之间是可以合并的,那么判断序列是否能分成两块即可。

C

比赛的时候傻了,其实是 d p dp dp 状态设计的不好。

发现题目等价于选出一些点,其花费为 b p k − b p 1 + ∑ i = 1 k a p k b_{p_k}-b_{p_1}+\sum_{i=1}^k a_{p_k} bpkbp1+i=1kapk,求最多能选出多少个点。

显然应该将点按 b b b 排序,因为走回头路一定不优。

f i , j f_{i,j} fi,j 表示前 i i i 个点取了 j j j 个,并强制取第 i i i 个点的最小花费,有初始化 f i , 1 = a i − b i f_{i,1}=a_i-b_i fi,1=aibi,转移 f i , j = min ⁡ k < i f k , j − 1 + a i f_{i,j}=\min_{k<i}{f_{k,j-1}+a_i} fi,j=mink<ifk,j1+ai,前缀 min ⁡ \min min 优化即可,最后判断 f i , j + b i ≤ l i m f_{i,j}+b_i \leq lim fi,j+bilim 即可。

D

最简单的一集。

这个东西显然是不好求的,而且长得很容斥,拆成总方案数 − - 和在集合 S S S 的方案数 − - 差在集合 S S S 的方案数 + + + 和差都在集合 S S S 的方案数。

这些东西都是非常好维护的,时间复杂度 O ( n ) \text{O}(n) O(n)

E

真降智吧。

考虑每一对 ( x , y ) (x,y) (x,y),公共前缀一定取,此时 x x x 的限制已经无效了,因为是根据 y y y 来取的。

我们考虑一个区间 [ l , r ] [l,r] [l,r],记有 k k k y i y_i yi j j j 位为 1 1 1,如果 k ≥ 2 k \geq 2 k2,那么直接令一个取 2 j 2^j 2j,一个取 2 j − 1 2^j-1 2j1 即可;如果 k = 1 k=1 k=1,那么就取 2 j 2^j 2j;如果 k = 0 k=0 k=0 那么跳过即可。

至于公共前缀,用 s t st st 表处理一下即可。

F

有一个很明显的想法就是连接形如 ( i , i + 1 ) (i,i+1) (i,i+1) 的边一定最优,代价下界显然为 d e g u − 1 deg_u-1 degu1

考虑 edge case,若当前删除点 u u u 1 1 1 n n n,那么可以设每个连通块的最大值为 m x i mx_i mxi,有构造连接所有的 ( m x i , m x i + 1 ) (mx_i,mx_i+1) (mxi,mxi+1),那么代价就为 d e g u − 1 deg_u-1 degu1

那么类似的,我们也希望形如这样构造其他情况,讨论 m x i mx_i mxi u u u 的关系:

若不存在 m x i mx_i mxi 满足 m x i + 1 = u mx_i+1=u mxi+1=u,那么直接像 u = 1 u=1 u=1 的情况构造即可。

否则我们考虑将整个序列 [ 1 , n ] [1,n] [1,n] 分为 [ 1 , u − 1 ] , [ u , u ] , [ u + 1 , n ] [1,u-1],[u,u],[u+1,n] [1,u1],[u,u],[u+1,n] 的形式,若所有连通块里的数都只在其中的一段区间内出现,那么考虑将 [ 1 , u − 1 ] , [ u + 1 , n ] [1,u-1],[u+1,n] [1,u1],[u+1,n] 分别连通,再连接 ( u − 1 , u + 1 ) (u-1,u+1) (u1,u+1),代价为 d e g u deg_u degu

若存在一个连通块内的数在 [ 1 , u − 1 ] , [ u + 1 , n ] [1,u-1],[u+1,n] [1,u1],[u+1,n] 内都出现过,就可以省去上述的最后一步连接,使得代价为 d e g u − 1 deg_u-1 degu1

考虑重新规划策略,设 m n i mn_i mni 为一个连通块内最小的数,对于只在 [ 1 , u − 1 ] [1,u-1] [1,u1] 内出现的块,连接 ( m n i − 1 , m n i ) (mn_i-1,mn_i) (mni1,mni);对于所有 m x i > u mx_i>u mxi>u 的块,连接 ( m x i , m x i + 1 ) (mx_i,mx_i+1) (mxi,mxi+1)

此时至少连接了 d e g u − 2 deg_u-2 degu2 条边,特判一下就好。

m n , m x mn,mx mn,mx 可以换根 dp 计算,时间复杂度 O ( n ) \text{O}(n) O(n)

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
const int N=2e5+5;
struct edge
{
	int next,to;
}e[N<<1];
vector<pii> vc[N];
int n,cnt,in[N],mn[N],mx[N],anss[N],deg[N];
int read()
{
	int res,f=1;
	char ch;
	while((ch=getchar())<'0'||ch>'9')
	if(ch=='-')
	f=-1;
	res=ch^48;
	while((ch=getchar())>='0'&&ch<='9')
	res=(res<<1)+(res<<3)+(ch^48);
	return res*f;
}
void add(int x,int y)
{
	e[++cnt].next=in[x];
	e[cnt].to=y;
	in[x]=cnt;
}
void dfs1(int u,int fa)
{
	int i,v;
	mn[u]=mx[u]=u;
	for(i=in[u];i;i=e[i].next)
	{
		v=e[i].to;
		if(v==fa)
		continue;
		dfs1(v,u);
		mn[u]=min(mn[u],mn[v]);
		mx[u]=max(mx[u],mx[v]);
	}
}
void dfs2(int u,int fa)
{
	int i,v;
	bool bz=0;
	for(i=in[u];i;i=e[i].next)
	{
		v=e[i].to;
		if(mx[v]==u-1)
		{
			bz=1;
			break;
		}
	}
	if(!bz||u==1||u==n)
	{
		anss[u]=deg[u]-1;
		for(i=in[u];i;i=e[i].next)
		{
			v=e[i].to;
			if(mx[v]<n&&mx[v]!=u-1)
			vc[u].push_back(make_pair(mx[v],mx[v]+1));
		}
	}
	else
	{
		for(i=in[u],bz=0;i;i=e[i].next)
		{
			v=e[i].to;
			if(mn[v]<u&&mx[v]>u)
			{
				bz=1;
				break;
			}
		}
		if(!bz)
		{
			anss[u]=deg[u];
			for(i=in[u];i;i=e[i].next)
			{
				v=e[i].to;
				if(mx[v]+1==u)
				vc[u].push_back(make_pair(mx[v],mx[v]+2));
				else
				if(mx[v]!=n)
				vc[u].push_back(make_pair(mx[v],mx[v]+1));
			}
		}
		else
		{
			int tmp=n;
			anss[u]=deg[u]-1;
			for(i=in[u];i;i=e[i].next)
			{
				v=e[i].to;
				if(mx[v]<u&&mn[v]!=1)
				vc[u].push_back(make_pair(mn[v]-1,mn[v]));
				if(mx[v]>u)
				{
					if(mx[v]!=n)
					vc[u].push_back(make_pair(mx[v],mx[v]+1));
					tmp=min(tmp,mn[v]);
				}
			}
			if(tmp<u&&tmp!=1)
			vc[u].push_back(make_pair(tmp-1,tmp));
		}
	}
	int mxm=0,mnm=1e9+7;
	for(i=in[u];i;i=e[i].next)
	{
		v=e[i].to;
		if(mx[v]!=n)
		mxm=max(mxm,mx[v]);
		if(mn[v]!=1)
		mnm=min(mnm,mn[v]);
	}
	for(i=in[u];i;i=e[i].next)
	{
		v=e[i].to;
		if(v==fa)
		continue;
		mx[u]=max(u,mx[v]==n?mxm:n);
		mn[u]=min(u,mn[v]==1?mnm:1);
		mx[v]=n;mn[v]=1;
		dfs2(v,u);
	}
}
void solve()
{
	int i,j,x,y;
	n=read();cnt=0;
	for(i=1;i<=n;i++)
	{
		in[i]=deg[i]=anss[i]=0;
		vc[i].clear();
	}
	for(i=1;i<=n-1;i++)
	{
		x=read();y=read();
		deg[x]++;deg[y]++;
		add(x,y);add(y,x);
	}
	dfs1(1,0);dfs2(1,0);
	for(i=1;i<=n;i++)
	{
		printf("%d %d\n",anss[i],(int)vc[i].size());
		for(j=0;j<(int)vc[i].size();j++)
		printf("%d %d\n",vc[i][j].first,vc[i][j].second);
	}
}
int main()
{
	int T=read();
	while(T--)
	solve();
	return 0; 
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值