JOISC2022

发布时间 2023-05-21 22:27:38作者: 暗蓝色的星空

[JOISC2022]监狱

容易发现一个人一旦开始走就不会停。
因此如果一个人的开始点 \(s_i\) 在另一个人的 \(s_j\to t_j\) 上,那么 \(i\) 的出发时间一定小于 \(j\),对于 \(t_i\) 在路径上同理。
因此可以建出一个图,那么合法当且仅当图是 \(DAG\)
建图可以用倍增优化建图,时空复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+7;
const int M = 2e7+7;
int n,m;
template <typename T>inline void read(T &x){
	x=0;char c=getchar();bool f=0;
	for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
	for(;c>='0'&&c<='9';c=getchar())
	x=(x<<1)+(x<<3)+(c^48);
	x=(f?-x:x);
}
vector<int> G[N];
int dep[N];
struct edge
{
	int y,next;
}e[M];
int link[M],deg[M],t=0;
int A[N][19],B[N][19],fa[N][19],tot=0;
void clr()
{
	for(int i=1;i<=n;i++)G[i].clear();
	for(int i=1;i<=n;i++)
	for(int k=0;k<=18;k++)
	A[i][k]=B[i][k]=fa[i][k]=0;
	for(int i=1;i<=tot;i++)
	link[i]=0,deg[i]=0;
	t=0;tot=0;
}
void add(int x,int y)
{
	e[++t].y=y;
	deg[y]++;
	e[t].next=link[x];
	link[x]=t;
}
void mo(int &x){if(!x)x=++tot;}
void dfs(int x,int pre)
{
	dep[x]=dep[pre]+1;
	fa[x][0]=pre;
	mo(A[x][0]);mo(B[x][0]);
	for(int k=1;fa[x][k-1];k++)
	{
		int y=fa[x][k-1];
		fa[x][k]=fa[y][k-1];
		if(!A[y][k-1])continue;
		mo(A[x][k]);add(A[x][k],A[x][k-1]);add(A[x][k],A[y][k-1]);
		mo(B[x][k]);add(B[x][k-1],B[x][k]);add(B[y][k-1],B[x][k]);
	}
	for(int y:G[x])
	{
		if(y==pre)continue;
		dfs(y,x);
	}
}
int LCA(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int k=18;k>=0;k--)
	if(dep[fa[x][k]]>=dep[y])x=fa[x][k];
	if(x==y)return x;
	for(int k=18;k>=0;k--)
	if(fa[x][k]!=fa[y][k])
	{
		x=fa[x][k];
		y=fa[y][k];
	}
	return fa[x][0];
} 
int nxto(int x,int y)
{
	if(x!=LCA(x,y))return fa[x][0];
	for(int k=18;k>=0;k--)
	if(dep[fa[y][k]]>dep[x])y=fa[y][k];
	return y;
}
void AddA(int x,int y,int o)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int k=18;k>=0;k--)
	if(dep[fa[x][k]]>=dep[y])
	{
		add(o,A[x][k]);
		x=fa[x][k];
	}
	if(x==y)
	{
		add(o,A[x][0]);
		return;
	}
	for(int k=18;k>=0;k--)
	if(fa[x][k]!=fa[y][k])
	{
		add(o,A[x][k]);
		add(o,A[y][k]);
		x=fa[x][k];
		y=fa[y][k];
	}
	add(o,A[x][1]);
	add(o,A[y][0]);
}
void AddB(int x,int y,int o)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int k=18;k>=0;k--)
	if(dep[fa[x][k]]>=dep[y])
	{
		add(B[x][k],o);
		x=fa[x][k];
	}
	if(x==y)
	{
		add(B[x][0],o);
		return;
	}
	for(int k=18;k>=0;k--)
	if(fa[x][k]!=fa[y][k])
	{
		add(B[x][k],o);
		add(B[y][k],o);
		x=fa[x][k];
		y=fa[y][k];
	}
	add(B[x][1],o);
	add(B[y][0],o);
}
queue<int> q;
void solve()
{
	clr();
	read(n);
	for(int i=1;i<n;i++)
	{
		int x,y;
		read(x);read(y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	tot=n;
	dfs(1,0);
	read(m);
	while(m--)
	{
		int x,y;
		read(x);read(y);
		int o=++tot;
		add(o,B[x][0]);add(A[y][0],o);
		AddB(nxto(x,y),y,o);
		AddA(nxto(y,x),x,o);
	}
	while(!q.empty())q.pop();
	for(int i=1;i<=tot;i++)
	if(!deg[i])q.push(i);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=link[x];i;i=e[i].next)
		{
			int y=e[i].y;
			deg[y]--;
			if(!deg[y])q.push(y);
		}
	}
//	printf("\n");
//	for(int x=1;x<=tot;x++)
//	if(deg[x])
//	for(int i=link[x];i;i=e[i].next)
//	{
//		int y=e[i].y;
//		if(deg[y])printf("%d %d\n",x,y);
//	}
	for(int i=1;i<=tot;i++)
	if(deg[i])
	{
		printf("No\n");
		return;
	}
	printf("Yes\n");
}
int main()
{
	int T;
	read(T);
	while(T--)
	{
		solve();
	}
	return 0;
}

[JOISC2022]京都观光

比较厉害的题。
发现路径可以分成若干个点,相邻两点之间只会拐一次,称这些点为关键点。
那么我们考虑从 关键点\((i,x)\) 到 关键点\((j,y)\)的路径。
如果这两个点相邻,那么有两种走法:
\(C_1=a_i(y-x)+b_y(j-i)\)\(C_2=a_j(y-x)+b_x(j-i)\)
如果不相邻,那不妨设经过了中间行 \(k\) (否则可以归纳),那么代价为 \(C_3=a_k(y-x)+b_x(k-i)+b_y(j-k)\)
容易发现,只有当 \(C3<C1\& C3<C2\)\(k\) 是有用的。
这等价于 $$\frac{a_k-a_i}{k-i} < \frac{b_y-b_x}{y-x}<\frac{a_j-a_k}{j-k}$$ 。
\((i,a_i)\) 看成一个点,那么相邻关键点的斜率是递增的,也就是说,我们最终的 \(a,b\) 都是下凸壳 。
具体的,我们先对 \(a,b\) 分别求下凸壳,最终的走法就是这两个凸壳的闵可夫斯基和。
复杂度 \(O(n+m)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+7;
int stk[N],top=0;
int ConVexHull(int *a,int n,int *A)
{
	top=0;
	for(int i=1;i<=n;i++)
	{
		while(top>1&&1ll*(a[stk[top]]-a[stk[top-1]])*(i-stk[top])>=1ll*(a[i]-a[stk[top]])*(stk[top]-stk[top-1]))top--;
		stk[++top]=i;
	}
	for(int i=1;i<=top;i++)A[i]=stk[i];
	return top; 
}
int A[N],B[N],a[N],b[N];
int n,m;
typedef long long LL;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)scanf("%d",&b[i]);
	n=ConVexHull(a,n,A);
	m=ConVexHull(b,m,B);
	int x=1,y=1;
	LL ans=0;
	for(int i=1;x<n||y<m;i++)
	{
		if(x==n)
		{
			ans+=1ll*a[A[x]]*(B[y+1]-B[y]);
			y++;
			continue;
		}
		if(y==m)
		{
			ans+=1ll*b[B[y]]*(A[x+1]-A[x]);
			x++;
			continue;
		}
		if(1ll*(a[A[x+1]]-a[A[x]])*(B[y+1]-B[y])<1ll*(b[B[y+1]]-b[B[y]])*(A[x+1]-A[x]))
		{
			ans+=1ll*b[B[y]]*(A[x+1]-A[x]);
			x++;
			continue;
		}
		ans+=1ll*a[A[x]]*(B[y+1]-B[y]);
		y++;
		continue;
	} 
	cout<<ans;
	return 0;
}