……(不知道说啥)
考试经过
开题感觉状态不错,发现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\)对应的数一致时才算本身矩形,否则算在附属举行中,看第二个样例:
这个可以被分成三个部分(L),左边的那个是上矩形,右下是右矩形,只有右上的是本身矩形
问题就是算一个L中每一行每一列满足最大值为\(s\)的答案,发现问题主要在本身矩形上,如果本体合法两翼也合法
那么就是算本体,不难想到容斥,钦定有i行不合法,所有列都合法,记方案数为\(f_i\),那么有
(其实是看\(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.多想能干什么,少想不能干什么,说不定分会更高