EPIC Institute of Technology Round August 2024 (Div. 1 + Div. 2)

Preface

两个礼拜前打的比赛拖到现在才写博客,我只能说也是个神人了

这场其实 D2 很快就想到做法了,但自己把自己给否了,后面不管了实现了一发交上去发现过了

然后这天由于 12 点左右室友就关灯睡觉了,我写完 D2 后看了眼 E 没仔细想就睡觉去了,后面发现 E 其实很 trivial


A. Distanced Coloring

签到,手玩发现答案为 \(\min(n,k)\times \min(m,k)\)

#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 pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,n,m,k;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	scanf("%d%d%d",&n,&m,&k),printf("%d\n",min(n,k)*min(m,k));
	return 0;
}

B. Removals Game

考虑在任意一个时刻,如果两个人对应的双端队列两端的元素不同,则先手一定获胜

模拟这个过程,如果两端的元素相同随便取哪一个都不会影响答案

#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 pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,n,x;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d",&n); deque <int> A,B;
		for (RI i=1;i<=n;++i) scanf("%d",&x),A.push_back(x);
		for (RI i=1;i<=n;++i) scanf("%d",&x),B.push_back(x);
		bool flag=0; while (A.size()>1)
		{
			int x=A.front(),y=A.back();
			if (x!=B.front()&&x!=B.back()) { flag=1; break; }
			if (y!=B.front()&&y!=B.back()) { flag=1; break; }
			A.pop_front();
			if (x==B.front()) B.pop_front(); else B.pop_back();
		}
		puts(flag?"Alice":"Bob");
	}
	return 0;
}

C. Black Circles

思博题,不难发现如果某个圆的圆心到终点的距离比起点到终点的距离短,则一定无解

反过来会发现这个圆一定追不上人,因此总可以设计一种路线来绕开它

#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 pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,x[N],y[N];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		scanf("%lld",&n);
		for (RI i=1;i<=n;++i) scanf("%lld%lld",&x[i],&y[i]);
		scanf("%lld%lld%lld%lld",&x[n+1],&y[n+1],&x[n+2],&y[n+2]);
		auto dist=[&](CI u,CI v)
		{
			return (x[u]-x[v])*(x[u]-x[v])+(y[u]-y[v])*(y[u]-y[v]);
		};
		int D=dist(n+1,n+2); bool flag=1;
		for (RI i=1;i<=n;++i)
		if (dist(i,n+2)<=D) { flag=0; break; }
		puts(flag?"YES":"NO");
	}
	return 0;
}

D1. DFS Checker (Easy Version)

要判断一个序列是否为合法的 DFS 序,很容易想到把每个点在 DFS 序中出现的位置作为其点权

那么此时一棵树满足条件当且仅当对于树上的每个点,其子树内的点权恰好构成一段连续区间 \([l,r]\),且当前点的权值恰为 \(l-1\)

等价转化后可以变成这样一个条件,维护每个点子树内点权的最小/最大值,判断这个值和当前点点权以及子树大小的关系即可

由于本题保证树是二叉树,因此直接暴力维护复杂度是 \(O(n\log^2 n)\) 的,可以通过

#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 pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=300005;
int t,n,q,x,y,fa[N],p[N],a[N],sz[N],ret;
vector <int> v[N]; set <int> son[N];
inline int check(CI now)
{
	return a[now]==*son[now].begin()&&a[now]+sz[now]-1==*son[now].rbegin();
}
inline void DFS(CI now=1,CI fa=0)
{
	sz[now]=1; son[now].insert(a[now]);
	for (auto to:v[now]) if (to!=fa)
	{
		DFS(to,now); sz[now]+=sz[to];
		for (auto tmp:son[to]) son[now].insert(tmp);
	}
	ret+=check(now);
}
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d",&n,&q); ret=0; fa[1]=0;
		for (RI i=1;i<=n;++i) v[i].clear(),son[i].clear();
		for (RI i=2;i<=n;++i) scanf("%d",&fa[i]),v[fa[i]].push_back(i);
		for (RI i=1;i<=n;++i) scanf("%d",&p[i]),a[p[i]]=i;
		for (DFS();q;--q)
		{
			scanf("%d%d",&x,&y);
			swap(p[x],p[y]); x=p[x]; y=p[y];
			vector <int> vec;
			for (int tmp=x;tmp;tmp=fa[tmp]) vec.push_back(tmp);
			for (int tmp=y;tmp;tmp=fa[tmp]) vec.push_back(tmp);
			sort(vec.begin(),vec.end());
			vec.erase(unique(vec.begin(),vec.end()),vec.end());
			for (auto p:vec) if (p) ret-=check(p);
			for (int tmp=x;tmp;tmp=fa[tmp]) son[tmp].erase(a[x]);
			for (int tmp=y;tmp;tmp=fa[tmp]) son[tmp].erase(a[y]);
			swap(a[x],a[y]);
			for (int tmp=x;tmp;tmp=fa[tmp]) son[tmp].insert(a[x]);
			for (int tmp=y;tmp;tmp=fa[tmp]) son[tmp].insert(a[y]);
			for (auto p:vec) if (p) ret+=check(p);
			puts(ret==n?"YES":"NO");
		}
	}
	return 0;
}

D2. DFS Checker (Hard Version)

上面的做法需要考虑一个点的整个子树,因此基本没有优化空间

但我们真的需要用到整个子树的信息吗,能不能只考虑当前点与它的儿子间的关系呢

一个直观的想法就是检验所有儿子中最小的点权是否为当前点点权加 \(1\),刚开始我感觉这样很对然后交上去发现 WA 了,遂发现还有形如以下这种情况

出错的原因本质上是由于只考虑最小的儿子,导致可能出现跳出当前子树又跳回来的情况

因此需要加上一个对于点权最大的儿子的 fix,用 set 维护儿子信息即可

#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 pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=300005;
int t,n,q,x,y,fa[N],p[N],a[N],sz[N],ret;
vector <int> v[N]; set <pi> son[N];
inline int check(CI now)
{
	if (son[now].empty()) return 1;
	return a[now]+1==son[now].begin()->fi&&a[now]+sz[now]-sz[son[now].rbegin()->se]==son[now].rbegin()->fi;
}
inline void DFS(CI now=1,CI fa=0)
{
	sz[now]=1; for (auto to:v[now]) if (to!=fa)
	DFS(to,now),sz[now]+=sz[to],son[now].insert({a[to],to});
	ret+=check(now);
}
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d",&n,&q); ret=0;
		for (RI i=0;i<=n;++i) v[i].clear(),son[i].clear();
		for (RI i=2;i<=n;++i) scanf("%d",&fa[i]),v[fa[i]].push_back(i);
		for (RI i=1;i<=n;++i) scanf("%d",&p[i]),a[p[i]]=i;
		for (DFS(),son[0].insert({a[1],1});q;--q)
		{
			scanf("%d%d",&x,&y);
			swap(p[x],p[y]); x=p[x]; y=p[y];
			vector <int> vec={x,y,fa[x],fa[y]};
			sort(vec.begin(),vec.end());
			vec.erase(unique(vec.begin(),vec.end()),vec.end());
			for (auto p:vec) if (p) ret-=check(p);
			son[fa[x]].erase({a[x],x});
			son[fa[y]].erase({a[y],y});
			swap(a[x],a[y]);
			son[fa[x]].insert({a[x],x});
			son[fa[y]].insert({a[y],y});
			for (auto p:vec) if (p) ret+=check(p);
			puts(ret==n?"YES":"NO");
		}
	}
	return 0;
}

E. Cosmic Rays

今天仔细想了下然后再加上祁神告诉我用个栈维护下就行,发现就是个煞笔题

考虑增量法,先求出之前若干个 pair 加入后的答案,以及用栈维护此时每个 pair 的存活时间

对于新加入的 pair,我们肯定是将其与栈顶的 pair 作比,若二者 \(b_i\) 相同显然可以合并,前面的就不用考虑了

否则若当前栈顶的存活时间小于等于当前 pair 的存活时间,则可以弹出栈顶,直到遇到某个类型相同的 pair 或者栈为空

要注意合并时还要顺带维护出弹掉的 pair 中存活时间的最大值以更新贡献,总复杂度 \(O(n)\)

#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 pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=300005;
int t,n,x,y; pi stk[N];
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		scanf("%lld",&n); int top=0,ans=0;
		for (RI i=1;i<=n;++i)
		{
			scanf("%lld%lld",&x,&y); int mx=0;
			while (top)
			{
				if (stk[top].se==y) x+=stk[top--].fi-mx;
				else if (stk[top].fi<=x) mx=max(mx,stk[top--].fi);
				else break;
			}
			stk[++top]={x,y}; ans=max(ans,x);
			printf("%lld%c",ans," \n"[i==n]);
		}
	}
	return 0;
}

F1. Court Blue (Easy Version) && F2. Court Blue (Hard Version)

很神秘的一个题,由于是补题就直接讲 F2 的做法了

这题的核心就是要想到当其中一个数是范围内最大的质数时,大概率是能满足 \(\gcd\)\(1\) 的限制的

因此 F1 直接求出最大和次大质数然后枚举即可,复杂度和范围内相邻的两个质数间的距离有关

对于 F2 的分析也是类似,但需要用 官方题解 中质数密度的思路证明暴力复杂度就是对的,剩下的就是记忆化实现一下

#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 pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=2e7+5,M=1500;
int t,n,m,l,f,vis[N],pri[N],cnt,ok[M][M];
LL ans; bool flag; vector <pi> rollback;
inline void init(CI n)
{
	vis[0]=vis[1]=1;
	for (RI i=2;i<=n;++i)
	{
		if (!vis[i]) pri[++cnt]=i;
		for (RI j=1;j<=cnt&&i*pri[j]<=n;++j)
		if (vis[i*pri[j]]=1,i%pri[j]==0) break;
	}
}
inline LL calc(CI q,CI p)
{
	LL res=1LL*q*l+1LL*p*f;
	if (p<=n) res=max(res,1LL*p*l+1LL*q*f);
	return res;
}
inline void DFS(CI q,CI p)
{
	if (__gcd(q,p)>1) return;
	ans=max(ans,calc(q,p));
	ok[n-q][m-p]=1; rollback.emplace_back(n-q,m-p);
	if (q<n&&!ok[n-q-1][m-p]) DFS(q+1,p);
	if (p==m) return (void)(flag=1);
	if (flag) return;
	if (p<m&&!ok[n-q][m-p-1]) DFS(q,p+1);
	if (flag) return;
}
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t),init(2e7);t;--t)
	{
		scanf("%d%d%d%d",&n,&m,&l,&f);
		int p=m; while (vis[p]) --p;
		ans=0; flag=0; rollback.clear();
		for (RI q=min(n,p-1);!flag;--q)
		{
			ans=max(ans,calc(q,p));
			if (p<m) DFS(q,p+1); else break;
		}
		for (auto [x,y]:rollback) ok[x][y]=0;
		printf("%lld\n",ans);
	}
	return 0;
}

Postscript

这两天也是摆烂摆爽了,每天就是下棋峡谷玩 Gal,但这来之不易的暑假好像也马上就要看到头了说是

posted @ 2024-08-25 17:38  空気力学の詩  阅读(134)  评论(0编辑  收藏  举报