20241020比赛总结

T1 Reverse

https://www.gxyzoj.com/d/hzoj/p/P980

假设1在点i时,这个1可以通过一次翻转到达那些点,将这些点和i连边,此时答案就是s到x的最短路

但是,此时边数也会到达\(n^2\)级别

考虑优化,因为边权均为1,所以可以直接bfs,可以发现每个点能转移的点的奇偶性是有限制的,而且每个点至多被更新一次

所以可以将所有点按奇偶性分类后丢进set,然后找到转移边界,暴力更新即可

注意,选出的子串的长度必须为k,所以在靠近两个端点的地方是不能取点作为中心的

代码:

#include<cstdio>
#include<set>
#include<queue>
#define it set<int>::iterator
using namespace std;
int n,m,s,k,b[100005],ans[100005];
double lmid,rmid;
set<int> s1,s2;
queue<int> q;
void bfs(int st)
{
	for(int i=1;i<=n;i++)
	{
		ans[i]=1e9;
	}
	ans[st]=0;
	q.push(st);
	if(st%2) s1.erase(st);
	else s2.erase(st);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		double ls=u-lmid,rs=rmid-u;
		int l=lmid-ls,r=rmid+rs;
		l=max(l,max(0,u-k+1)),r=min(r,min(n,u+k-1));
	//	printf("%d %d %d %d\n",u,l,r,ans[u]);
		if((u+k+1)%2)
		{
			it lid=s1.lower_bound(l);
			it rid=s1.lower_bound(r);
			while(*lid<=r&&lid!=s1.end())
			{
				if(b[*lid])
				{
					lid++;
					continue;
				}
				ans[*lid]=ans[u]+1;
				q.push(*lid);
				lid++;
			}
		//	printf("1");
			lid=s1.lower_bound(l);
			if(*rid<=r&&rid!=s1.end()) rid++;
			s1.erase(lid,rid);
		}
		else
		{
			it lid=s2.lower_bound(l);
			it rid=s2.lower_bound(r);
			while(*lid<=r&&lid!=s2.end())
			{
				if(b[*lid])
				{
					lid++;
					continue;
				}
				ans[*lid]=ans[u]+1;
				q.push(*lid);
				lid++;
			}
			lid=s2.lower_bound(l);
			if(*rid<=r&&rid!=s2.end()) rid++;
			s2.erase(lid,rid);
		}
	}
}
int main()
{
	freopen("reverse.in","r",stdin);
	freopen("reverse.out","w",stdout);
	scanf("%d%d%d%d",&n,&k,&m,&s);
	lmid=(1+k)*1.0/2.0,rmid=(n+n-k+1)*1.0/2.0;
	for(int i=1;i<=m;i++)
	{
		int x;
		scanf("%d",&x);
		b[x]=1;
	}
	for(int i=1;i<=n;i++)
	{
		if(i%2) s1.insert(i);
		else s2.insert(i);
	}
	bfs(s);
	for(int i=1;i<=n;i++)
	{
		if(ans[i]!=1e9) printf("%d ",ans[i]);
		else printf("-1 ");
	}
	return 0;
}

T2 Silhouette

https://www.gxyzoj.com/d/hzoj/p/980

显然点\((i,j)\)上只能有\(min(a_i,b_j)\)块,所以可以从大到小枚举,所有已选的行和列的交叉点就是可以放的地方

将行和列按从小到大排列后,每次增加的交叉点就会形成L形,如图

此时,对于红色区域,在前面已经处理完毕,所以蓝色区域已经满足列,绿色区域已经满足行

\(f(i)\)为至少i行不满足条件的情况数,则:

\[f_i=C_a^i\times (s^i\times((s+1)^{a+c-i}-s^{a+c-i}))^b\times (s^i\times(s+1)^{a+i})^d \]

所以符合条件的情况数为

\[\sum_{i=0}^a (-1)^i \times f(i) \]

代码:

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=1e9+7;
int n,a[100005],b[100005],c[200005];
ll qpow(ll x,int y)
{
	ll res=1;
	while(y)
	{
		if(y&1) res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
ll fac[100005],inv[100005];
ll C(int n,int m)
{
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
ll solve(int a,int b,int c,int d,int s)
{
	ll res=0;
	for(int i=0;i<=a;i++)
	{
		ll x=C(a,i)*qpow(qpow(s,i)*(qpow(s+1,a+c-i)-qpow(s,a+c-i)+mod)%mod,b)%mod;
		x=x*qpow(qpow(s,i)*qpow(s+1,a-i)%mod,d)%mod;
		x=(x+mod)%mod;
		if(i%2) res=(res-x+mod)%mod;
		else res=(res+x)%mod;
	}
	return res;
}
int main()
{
	freopen("silhouette.in","r",stdin);
	freopen("silhouette.out","w",stdout);
	scanf("%d",&n);
	fac[0]=inv[0]=1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		fac[i]=fac[i-1]*i%mod;
		c[i]=a[i];
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&b[i]);
		c[i+n]=b[i];
	}
	inv[n]=qpow(fac[n],mod-2);
	for(int i=n-1;i>0;i--)
	{
		inv[i]=inv[i+1]*(i+1)%mod;
	}
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	if(a[n]!=b[n])
	{
		printf("0");
		return 0;
	}
	sort(c+1,c+n*2+1);
	int m=unique(c+1,c+2*n+1)-c-1;
	int pa=n+1,pb=n+1,xa=n,xb=n;
	ll ans=1;
	for(int i=m;i>=0;i--)
	{
		while(xa-1&&a[xa-1]==c[i]) xa--;
		while(xb-1&&b[xb-1]==c[i]) xb--;
		ans=ans*solve(pa-xa,pb-xb,n-pa+1,n-pb+1,c[i])%mod;
	//	printf("%lld\n",ans);
		pa=xa,pb=xb;
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2024-10-20 16:17  wangsiqi2010916  阅读(11)  评论(0编辑  收藏  举报