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;
}