模拟49 考试总结

……(不知道说啥)

考试经过

开题感觉状态不错,发现T1BFS,直接打,写完了跑一遍大样例把电脑跑死了,重启发现不对,输出了半天之后发现边界没有对,改了一种做法发现大样例0.01s就过了,手造几个数据最慢卡到4s,小卡一下常就交了
T2和T3似乎都不太可做,T2感觉要容斥但看着像复杂dp就没打,T3只会暴力,于是写了dfs和状压
得分57+17+24=98,和估分一样,暴力似乎打满了水到rank 7,前面的T1都切了,瑟瑟发抖

T1.Reverse

显然可以bfs,注意这里要选择区间所以区间的左右端点有限制,并不是只要1能翻过去就行
枚举选择区间的起始位置,注意上下界,这里由于一个点只会被更新一次,之后他就和禁止点没有区别了,所以可以标记一下,减小if判断的常数
相当与spfa,复杂度大约是\(nk\),卡一下就能过

#include <bits/stdc++.h>
using namespace std;
#define R register
inline int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
const int N=100050;
bool p[N];int n,k,m,s;
queue <int> q;
int d[N];
inline void bfs(int ga)
{
	memset(d,0x3f,sizeof(d));
	d[ga]=0;q.push(ga);p[s]=1;
	while(q.size())
	{
		int x=q.front();q.pop();
		int y1,y2,ll=max((int)1,x-k+1),rr=min(x,n-k+1);
		for(R int i=ll;i<=rr;i++)
		{
			int pp=i+k-1-(x-i);
			if(p[pp]==0)
			{
				d[pp]=d[x]+1;
				p[pp]=1;
				q.push(pp);
			}
		}
	}
}
signed main()
{
	n=read(),k=read(),m=read(),s=read();
	for(int i=1;i<=m;i++)
	{	
		int x=read();
		p[x]=1;
	}
	bfs(s); 
	for(R int i=1;i<=n;i++)
	{
		if(d[i]>1e8)printf("-1 ");
		else printf("%d ",d[i]);
	}
	return 0;
}

正解其实有\(O(n)\)做法,类似用链表,每次将一个区间的下一个节点都指向区间末尾即可,这样不会重复搜到,跑的飞快

#include <bits/stdc++.h>
using namespace std;
#define R register
inline int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
const int N=100050;
bool p[N];int n,k,m,s;
queue <int> q;
int d[N],next[N];
inline void bfs(int ga)
{
	memset(d,0x3f,sizeof(d));
	d[ga]=0;q.push(ga);p[s]=1;
	for(int i=1;i<=n;i++)next[i]=i+2;
	while(q.size())
	{
		int x=q.front(),pp;q.pop();
		int ll=max((int)1,x-k+1),rr=min(x,n-k+1);
		for(R int i=2*ll+k-x-1;i<=2*rr+k-x-1;i=pp)
		{	
			pp=next[i];next[i]=next[2*rr+k-x-1];
			if(p[i])continue;
			d[i]=d[x]+1;
			p[i]=1;q.push(i);
		}
	}
}
signed main()
{
	n=read(),k=read(),m=read(),s=read();
	for(int i=1;i<=m;i++)
	{	
		int x=read();
		p[x]=1;
	}
	bfs(s); 
	for(R int i=1;i<=n;i++)
	{
		if(d[i]>1e8)printf("-1 ");
		else printf("%d ",d[i]);
	}
	return 0;
}

这种优化技巧简单实用,需要掌握

T2. Silhouette

就是个简单的数数题,然而我第一步都没想到
首先可以给两个数组排序,不影响结果,因为一个限制是对于一行或一列而言的,将它上下或左右交换之后限制仍然成立,可以感性理解
接下来对于每个格子它最大能填的数是\(\max(a_i,b_j)\),从小到大枚举\(A,B\),考虑他可以管辖的范围,也就是什么范围内的最大可以填的数都是它,发现这个一定构成一个L形或者矩形(特殊情况),我们关注它的构造方式
设中间矩形有\(a\)行\(b\)列,上面多出来\(c\)行,下面多出来\(d\)列,具体实现跳指针就行,可以找到这个当前考虑形状
注意这个L是由一个本身的矩形和一个上面的矩形和一个右面的矩形构成的,当且仅当\(A,B\)对应的数一致时才算本身矩形,否则算在附属举行中,看第二个样例:
模拟49 考试总结
这个可以被分成三个部分(L),左边的那个是上矩形,右下是右矩形,只有右上的是本身矩形
问题就是算一个L中每一行每一列满足最大值为\(s\)的答案,发现问题主要在本身矩形上,如果本体合法两翼也合法
那么就是算本体,不难想到容斥,钦定有i行不合法,所有列都合法,记方案数为\(f_i\),那么有

\[f_i=\dbinom{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 \]

(其实是看\(skyh\)学长的,不过似乎那里把\(a-i\)写成了\(c-i\))
如何得到呢?首先从\(a\)行选\(i\)行肯定是组合数,其中\(i\)行由于强制不合法所以要选的最大值比\(s\)小,共\(s\)种选择(可以有0),剩下的要出现\(s\),所以总的减不合法的,最后一共\(b\)列所以是次方,至于\(c,d\)不用容斥,直接乘进柿子里就行,仔细想一下就理解了
然后容斥系数当然是\((-1)^i\),最后所有块的\(ans\)相乘就是答案了

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=100050;
const int mod=1e9+7;
int a[N],b[N];
inline int ksm(int x,int y)
{
	int s=1;
	for(;y;y>>=1)
	{	
		if(y&1)s=s*x%mod;
		x=x*x%mod;
	}
	return s;
}
int inv[N],jc[N],jcny[N];
inline int C(int x,int y)
{	
	if(!y)return 1;
	if(x<y)return 0;
	return jc[x]*jcny[y]%mod*jcny[x-y]%mod;
}
signed main()
{
	int n;cin>>n;
	memset(a,0x3f,sizeof(a));memset(b,0x3f,sizeof(b));
	inv[1]=1;jc[0]=1;jc[1]=1;jcny[0]=1;jcny[1]=1;
   for(int i=2;i<=n;i++)
   {
     jc[i]=jc[i-1]*i%mod;
     inv[i]=(mod-mod/i)*inv[mod%i]%mod; 
     jcny[i]=jcny[i-1]*inv[i]%mod;
	} 
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
	sort(a+1,a+n+1);sort(b+1,b+n+1);
	if(a[n]!=b[n]){puts("0");return 0;}
	int p1=1,p2=1,ans=0;
	while(p1<=n&&p2<=n)
	{
		int mi=min(a[p1],b[p2]),s=1e9;
		int aa=0,bb=0,c=0,d=0;
		while(a[p1]==mi)s=min(s,a[p1]),p1++,bb++;
		while(b[p2]==mi)s=min(s,b[p2]),p2++,aa++;
		if(aa)d=n-(p1-1);if(bb)c=n-(p2-1);
		int an=0;
		for(int i=0;i<=aa;i++)
		{
			int ann=ksm(ksm(s,i)*((ksm(s+1,aa+c-i)-ksm(s,aa+c-i)+mod)%mod)%mod,bb)*ksm(ksm(s,i)*ksm(s+1,aa-i)%mod,d)%mod;
			if(i&1)an=(an-ann*C(aa,i)%mod+mod)%mod;
			else an=(an+ann*C(aa,i)%mod)%mod;
		}
		if(!ans)ans=an;
		else ans=ans*an%mod;
	}
	cout<<ans<<endl;
	return 0;
}

T3.Seat

神仙题,正在研究std,咕

考试总结

1.卡常一定要到位,尤其这种十分可乱搞的题,有想法别不敢打
2.多想能干什么,少想不能干什么,说不定分会更高

上一篇:DHCP与DNS---(钓鱼网站原理)


下一篇:C#开发BIMFACE系列49 Web网页中加载模型与图纸的技术方案