Codeforces Round #809 (Div. 2)

Preface

由于受YKH之约7/31号晚上要打CF,所以赶紧打点CF复健

这场比赛只有Div2,很适合现在的我找找自信(被虐)


A. Another String Minimization Problem

不难发现每对位置之间相互独立,因此对于每一对贪心地先放前面的A,有了再扔后面即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=55;
int t,n,m,x,vis[N];
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%d",&n,&m),i=1;i<=m;++i) vis[i]=0;
		for (i=1;i<=n;++i)
		{
			scanf("%d",&x); int mi=min(x,m+1-x),mx=max(x,m+1-x);
			if (!vis[mi]) vis[mi]=1; else vis[mx]=1;
		}
		for (i=1;i<=m;++i) putchar(vis[i]?'A':'B'); putchar('\n');
	}
	return 0;
}

B. Making Towers

把每种颜色的位置单独拿出来看,不难发现只要对于两个位置\(x,y(x<y)\),只要\(y-x\)是奇数就可以把这两个位置连起来

\(f_{i}\)表示以\(i\)为结尾时的答案,不难发现转移的时候我们只关心\(f_{j}(j<i\text{且j和i的奇偶性不同})\)的最大值

因此直接用两个数记录一下下标是奇数和偶数时的情况即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,x,f[N][2];
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),i=1;i<=n;++i) f[i][0]=f[i][1]=0;
		for (i=1;i<=n;++i)
		scanf("%d",&x),f[x][i&1]=f[x][(i&1)^1]+1;
		for (i=1;i<=n;++i)
		printf("%d%c",max(f[i][0],f[i][1]),i==n?'\n':' ');
	}
	return 0;
}

C. Qpwoeirut And The City

首先考虑满足题意的房子数量最大,一眼奇偶分类

\(n\)为奇数时,显然所有的偶数下标的房子都得是cool的,直接算即可

\(n\)为偶数时,我们把中间的数相邻两个看成一组,假设在这一组中选前一个变cool是操作1,选后一个变cool是操作2

那么显然一种合法方案一定是一段连续的1再接上一段连续的2

直接前缀后缀贡献统计后枚举即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,h[N]; long long pre[N],suf[N],ans;
inline int fix(CI x)
{
	return max(max(h[x-1],h[x+1])-h[x]+1,0);
}
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&h[i]); 
		if (ans=0,n&1)
		{
			for (i=2;i<n;i+=2) ans+=fix(i);
		} else
		{
			for (i=2;i<n;i+=2) pre[i>>1]=pre[i-2>>1]+fix(i);
			for (suf[n>>1]=0,i=n-1;i>1;i-=2) suf[i>>1]=suf[i+2>>1]+fix(i);
			for (ans=1e18,i=0;i<(n>>1);++i) ans=min(ans,pre[i]+suf[i+1]);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

D1. Chopping Carrots (Easy Version)

数据范围明显\(O(n^2)\),直接考虑暴力

一个显然的想法,我们暴枚\(\min_{1\le i\le n} (\lfloor \frac{a_i}{p_i} \rfloor)\)的值\(x\),考虑对于每个数\(a_i\),在满足\(\lfloor \frac{a_i}{p_i} \rfloor\ge x\)的条件下\(p_i\)的最大值,此时显然\(\lfloor \frac{a_i}{p_i} \rfloor\)的值最小

对于\(\lfloor \frac{a_i}{p_i} \rfloor\ge x\),显然可转化为\(\lfloor \frac{a_i}{x} \rfloor\ge p_i\),即此时\(p_i\)\(\min(\lfloor \frac{a_i}{x} \rfloor,k)\),对所有\(a_i\)的情形取个最大值即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=3005;
int t,n,m,ans,a[N];
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&a[i]);
		for (ans=a[n],i=0;i<=a[1];++i)
		{
			int pos=0; for (j=1;j<=n;++j)
			pos=max(pos,a[j]/min(i?a[j]/i:m,m)); ans=min(ans,pos-i);
		}
		printf("%d\n",ans);
	}
	return 0;
}

D2. Chopping Carrots (Hard Version)

水平退化到一个除法分块调了半天真是醉了……

考虑在D1的基础上优化,我们先来找下性质

首先不难发现对于每一个\(a_i\),它整除后得到的数是\(\sqrt a_i\)级别的,我们假设这些数为\(s_1,s_2,\dots,s_k(s_1<s_2<\cdots<s_k)\)

由于每个\(a_i\)相互独立,因此我们可以把问题转化为:

一个数轴上有若干点,点的坐标就是\(s_j\),同时每个点都有一个颜色\(i\)

现在要找一个最小的区间满足在这个区间内包含了所有颜色的点

不难想到用\(f_i\)表示\(i\)为左端点时合法区间右端点的最小值,然后每次枚举一个\(a_i\)求出它的\(s\)数组更新\(f\)数组即可

考虑对于一对相邻的\(s_j,s_{j+1}\),显然有转移\(f_i=\max(f_i,s_{j+1}),i\in (s_j,s_{j+1}]\)

然后我们发现一个显然的性质,\(f_i\)非降的,因此我们可以把修改打一个标记在\(s_j+1\)处,最后用前缀\(\max\)统计下即可

时间复杂度\(O(n\sqrt a_n)\)

#include<cstdio>
#include<vector>
#include<algorithm>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
typedef vector <int>::iterator VI;
const int N=100005;
int t,n,m,ans,a[N],pos[N];
vector <int> s; VI L,R;
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i,l,r; for (scanf("%d%d",&n,&m),i=1;i<=n;++i)
		scanf("%d",&a[i]); for (i=0;i<=a[n];++i)
		pos[i]=i; for (i=1;i<=n;++i)
		{
			s.clear(); for (l=1;l<=min(m,a[i]);l=r+1)
			{
				int ret=a[i]/l; s.push_back(ret);
				r=ret?a[i]/ret:a[i];
			}
			s.push_back(0); reverse(s.begin(),s.end()); s.push_back(1e9);
			for (L=R=s.begin(),++R;R!=s.end();++L,++R) 
			pos[*L+1]=max(pos[*L+1],*R);
		}
		for (ans=1e9,i=1;i<=a[n];++i)
		pos[i]=max(pos[i],pos[i-1]),ans=min(ans,pos[i]-i);
		printf("%d\n",ans);
	}
	return 0;
}

E. Qpwoeirut and Vertices

这是Div2的题吗?有点难度的说可能是因为现在打个LCA都费劲吧

首先我们要知道一个叫Kruskal重构树的东西(话说我之前学OI的时候都不会),它在处理图上带阈值联通问题的一大利器

当我们建出重构树后,对每个虚拟节点赋以边的标号为权值

不难发现两点\(x,y\)的最小联通代价就是它们LCA的权值

E.g. 对于样例

Pic1

我们可以建出它的Kruskal重构树(方点代表虚拟节点)

例如对于2,3两点,它们的LCA是7点,权值为2,因此至少需要前两条边才能联通

接下来考虑询问,不难发现它的要求是这个区间内的所有点都在一个连通块内

我们不妨假设\(f_i=Val_{\operatorname{LCA(i,i+1)}}\),那么根据联通的转递性对于一个询问\(l,r\),最后的答案就是\(\max_{l\le i<r} f_i\)

直接预处理出\(f_i\)之后用RMQ处理询问即可,注意特判\(l=r\)的情形

复杂度\(O(n\log n)\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
struct edge
{
	int x,y;
}e[N]; int t,n,m,q,l,r,tot,val[N];
namespace DSU
{
	int fa[N];
	inline void clear(void)
	{
		for (RI i=1;i<=n;++i) fa[i]=i;
	}
	inline int getfa(CI x)
	{
		return fa[x]==x?x:fa[x]=getfa(fa[x]);
	}
	inline bool query(CI x,CI y)
	{
		return getfa(x)==getfa(y);
	}
};
namespace T
{
	struct edge
	{
		int to,nxt;
	}e[N<<1]; int cnt,head[N],dep[N],anc[N][20];
	inline void addedge(CI x,CI y)
	{
		e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
		e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
	};
	inline void clear(void)
	{
		RI i,j; for (i=1;i<=tot;++i) for (j=0;j<19;++j)
		if (anc[i][j]) anc[i][j]=0; else break;
		for (i=1;i<=tot;++i) head[i]=val[i]=0; cnt=0; tot=n;
	}
	inline void link(int x,int y,CI id)
	{
		val[++tot]=id; DSU::fa[tot]=tot; x=DSU::getfa(x); y=DSU::getfa(y);
		addedge(x,tot); addedge(y,tot); DSU::fa[x]=DSU::fa[y]=tot;
	}
	#define to e[i].to
	inline void DFS(CI now=tot,CI fa=0)
	{
		dep[now]=dep[anc[now][0]=fa]+1; RI i; for (i=0;i<19;++i)
		if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break;
		for (i=head[now];i;i=e[i].nxt) if (to!=fa) DFS(to,now);
	}
	inline void DEBUG(CI now=tot,CI fa=0)
	{
		printf("%d : ",now); RI i;
		for (i=head[now];i;i=e[i].nxt) if (to!=fa) printf("%d ",to); putchar('\n');
		for (i=head[now];i;i=e[i].nxt) if (to!=fa) DEBUG(to,now);
	}
	#undef to
	inline int getlca(int x,int y)
	{
		if (dep[x]<dep[y]) swap(x,y); RI i;
		for (i=19;~i;--i) if (dep[anc[x][i]]>=dep[y]) x=anc[x][i];
		if (x==y) return x; for (i=19;~i;--i)
		if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
		return anc[x][0];
	}
};
namespace RMQ
{
	int log[N],mx[N][20];
	inline void init(void)
	{
		RI i,j; for (i=2;i<n;++i) log[i]=log[i>>1]+1;
		for (i=1;i<n;++i) mx[i][0]=val[T::getlca(i,i+1)];
		for (j=1;(1<<j)<n;++j) for (i=1;i+(1<<j)-1<n;++i)
		mx[i][j]=max(mx[i][j-1],mx[i+(1<<j-1)][j-1]);
	}
	inline int query(CI l,CI r)
	{
		int k=log[r-l+1]; return max(mx[l][k],mx[r-(1<<k)+1][k]);
	}
};
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; scanf("%d%d%d",&n,&m,&q); DSU::clear(); T::clear();
		for (i=1;i<=m;++i) scanf("%d%d",&e[i].x,&e[i].y);
		for (i=1;i<=m;++i) if (!DSU::query(e[i].x,e[i].y)) T::link(e[i].x,e[i].y,i);
		for (T::DFS(),RMQ::init(),i=1;i<=q;++i)
		scanf("%d%d",&l,&r),printf("%d%c",l==r?0:RMQ::query(l,r-1),i==q?'\n':' ');
	}
	return 0;
}

Postscript

现在水平确实不行,D2和E都做不来,太菜太菜

posted @ 2022-07-29 17:20  空気力学の詩  阅读(83)  评论(0编辑  收藏  举报