Codeforces Round 911 (Div. 2)

Preface

忙里偷闲补一下之前欠下的一些CF

这场前5个题都极其一眼,然而F瞪了好久愣是屁都不会

感觉现在水平有有点到瓶颈了,以前是Div2D写完卡现在是Div2E写完卡,但至少还是在进步的


A. Cover in Water

如果存在某个空地块的长度大于\(2\)则可以用两个块造出无限水,否则答案就是所有空位的个数

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=105;
int t,n; char s[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; int lst,sum=0,flag=0;
		for (scanf("%d%s",&n,s+1),i=1;i<=n;++i)
		{
			if ((i==1||s[i-1]=='#')&&s[i]=='.') lst=i;
			if (s[i]=='.'&&(i==n||s[i+1]=='#'))
			{
				int len=i-lst+1; if (len>2) flag=1; sum+=len;
			}
		}
		printf("%d\n",flag?2:sum);
	}
	return 0;
}

B. Laura and Operations

能变成某一个数的充要条件是,另外两个数的奇偶性相同

构造一组解的话也很简单,首先把另外两个数中的一个用到\(0\),这样多余的那个个数一定是偶数

不妨设要变成\(1\),现在\(2\)还有偶数个,\(3\)已经用完了,我们重复若干次\((1,2)\to 3,(2,3)\to 1\)即可,每次操作可以消去\(2\)\(2\)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=105;
int t,a[3];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (i=0;i<3;++i) scanf("%d",&a[i]);
		for (i=0;i<3;++i) putchar(a[(i+1)%3]%2==a[(i+2)%3]%2?'1':'0'),putchar(" \n"[i==2]);
	}
	return 0;
}

C. Anji's Binary Tree

直接在数上DP一下即可,设\(f_x\)表示走到\(x\)后要走到某个叶子节点最少所需修改次数

转移的话非常显然,注意当字符为L/R时走对应一边时不需要额外的修改代价,否则要带上\(1\)的修改代价

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=300005,INF=1e9;
int t,n,ls[N],rs[N],f[N]; char s[N];
inline void DFS(CI now=1)
{
	if (ls[now]==0&&rs[now]==0) return (void)(f[now]=0); f[now]=INF;
	if (ls[now]) DFS(ls[now]); if (rs[now]) DFS(rs[now]);
	if (s[now]=='U') f[now]=min(f[now],min(f[ls[now]],f[rs[now]])+1);
	if (s[now]=='L') f[now]=min(f[now],min(f[ls[now]],f[rs[now]]+1));
	if (s[now]=='R') f[now]=min(f[now],min(f[ls[now]]+1,f[rs[now]]));
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%s",&n,s+1),i=1;i<=n;++i) scanf("%d%d",&ls[i],&rs[i]);
		f[0]=INF; DFS(); printf("%d\n",f[1]);
	}
	return 0;
}

D. Small GCD

很经典的约数容斥,看完题目就能想到

首先考虑将所有数从小到大排序,则此时相当于要计算:

\[\sum_{i=1}^n \sum_{j=i+1}^n \gcd(a_i,a_j)\times (n-j) \]

乍一看不好计算,但有关\(\gcd\)的东西我们要么想到反演,要么就是约数容斥,这里显然是后者

我们枚举某个约数\(d\),将序列中满足\(d|a_i\)的位置\(i\)按顺序拿出来,此时整个式子中除了\(\gcd(a_i,a_j)\)外的贡献很容易统计

然后套约数容斥的板子,把每个数的真实贡献算清楚即可,具体实现看代码,复杂度\(O(n\sqrt a_i)\)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,m,a[N],f[N]; vector <int> v[N];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i,j; for (m=0,scanf("%lld",&n),i=1;i<=n;++i)
		scanf("%lld",&a[i]),m=max(m,a[i]);
		for (i=1;i<=m;++i) f[i]=0,v[i].clear();
		for (sort(a+1,a+n+1),i=1;i<=n;++i)
		for (j=1;j*j<=a[i];++j) if (a[i]%j==0)
		{
			v[j].push_back(i);
			if (j!=a[i]/j) v[a[i]/j].push_back(i);
		}
		int ans=0; for (i=m;i>=1;--i)
		{
			for (j=0;j<v[i].size();++j) f[i]+=j*(n-v[i][j]);
			for (j=2*i;j<=m;j+=i) f[i]-=f[j]; ans+=f[i]*i;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

E. Transitive Graph

这道更是傻逼题

有向图的传递闭包?那不就等价于跑个Tarjan缩点,在一个强连通分量里的所有点最后会变成一个团吗

然后又要求路径最长,那么显然一个强连通分量里的点最后都要选上

最后跑个拓扑排序,过程中维护下信息最优即可

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int t,n,m,a[N],x,y,dfn[N],low[N],stk[N],vis[N],col[N],tot,top,cnt,s[N],sz[N],deg[N];
pi f[N]; vector <int> v[N],nv[N];
inline void Tarjan(CI now)
{
	dfn[now]=low[now]=++tot; stk[++top]=now; vis[now]=1;
	for (int to:v[now]) if (!dfn[to]) Tarjan(to),low[now]=min(low[now],low[to]);
	else if (vis[to]) low[now]=min(low[now],dfn[to]);
	if (low[now]==dfn[now])
	{
		s[col[now]=++cnt]=-a[now]; sz[cnt]=1; vis[now]=0;
		while (stk[top]!=now) s[col[stk[top]]=cnt]-=a[stk[top]],++sz[cnt],vis[stk[top--]]=0; --top;
	}
}
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&a[i]);
		for (i=1;i<=m;++i) scanf("%d%d",&x,&y),v[x].push_back(y);
		for (i=1;i<=n;++i) if (!dfn[i]) Tarjan(i);
		for (i=1;i<=n;++i) for (auto j:v[i])
		if (col[i]!=col[j]) nv[col[i]].push_back(col[j]),++deg[col[j]];
		for (i=1;i<=cnt;++i) if (!deg[i]) f[i]=pi(sz[i],s[i]); else f[i]=pi(0,0);
		queue <int> q; for (i=1;i<=cnt;++i) if (!deg[i]) q.push(i);
		while (!q.empty())
		{
			int now=q.front(); q.pop();
			for (auto to:nv[now])
			{
				f[to]=max(f[to],pi(f[now].fi+sz[to],f[now].se+s[to]));
				if (!--deg[to]) q.push(to);
			}
		}
		pi ans=pi(0,0); for (i=1;i<=cnt;++i) ans=max(ans,f[i]);
		printf("%lld %lld\n",ans.fi,-ans.se);
		for (i=1;i<=n;++i) v[i].clear(),dfn[i]=0;
		for (i=1;i<=cnt;++i) nv[i].clear(),deg[i]=0; tot=cnt=0;
	}
	return 0;
}

Postscript

评价是题出的很好,希望下次我现场打的时候也能出这么合我胃口的DE,我可以展示1h切5题光速下班

posted @ 2023-12-03 20:25  空気力学の詩  阅读(37)  评论(0编辑  收藏  举报