QOJ-7650 题解-超级分讨
QOJ-7650
你说得对,但是我常常追忆过去,生命瞬间定格在脑海。我将背后的时间裁剪、折叠、蜷曲,揉捻成天上朵朵白云。
《没有创意的题目名称》
题目简述
给点正整数 \(n (1 \le n \le 2000)\),和一个长度为 \(n+1\) 的序列 \(lim_0, ...... ,lim_n\)。你需要构造一个数组 \(f\),满足:
- 对于所有 \(0 \le i \le n\),有 \(0 \le f_i \le lim_i (0 \le lim_i \le n)\)
- 对于每个 \(0 \le i \le n\),对于所有 \(0 \le j\le i\) 存在 \(f_{f_j+f_{i-j}} =f_i\)。
- 对于 \(i > n\),\(f_i=-1\)
询问构造方法的数量,对 \(998244353\) 取模。
问题分析
我们定义 \(i \sim j\) 当且仅当 \(f_i=f_j\)。
显然对于每个 \(0 \le i \le n\),对于所有 \(0 \le j\le i\) 存在 \(f_{f_j+f_{i-j}} =f_i\) 可以描述为 对于任意 \(0 \le i \le n,0\le j \le n-i\),满足 \(f_i+f_j \sim i+j\)。
显然,题目条件可以等价为下列三个条件。
- 条件一:对于所有 \(0 \le i \le n\),有 \(0 \le f_i \le lim_i\)
- 条件二:对于每个 \(0 \le i \le n,0\le j \le n-i\),满足 \(f_i+f_j \le n\)。
- 条件三:对于每个 \(0 \le i \le n,0\le j \le n-i\),满足 \(f_i+f_j \lim i+j\)。
当 \(f\) 两两不同时,\(i \sim j\) 等价为 \(i=j\)。带入 \(i=0,j=0\),存在 \(f_0=0\)。因为 \(f_0+f_i=i\),所以 \(f_i=i\)。
假若存在相同元素,另 \(p\) 为最小的满足 \(\exists q,p < q \le n,f_p=f_q\),存在 \(p\sim q \Rightarrow f_p=f_q \Rightarrow f_p+f_1=f_q+f_1 \Rightarrow p+1\sim q+1\)。因此$f_p,f_{p+1},......,f_q $ 为 \(f_p,......,f_n\) 的最小循环节(可能不完整)。
接下来,开始分讨!
1. \(f_0 = 0\)
若 \(f_0=0\) ,类似于上文,对于 \(0\le i\le p-1,f_i=i\)。类似的,\(p\le i \le q-1,f_i \ge i\)。(考虑带入 \(f_0\),易证)
同时,对于 \(p \le i \le q-1\),存在\(k | f_i-i\)(因为当且仅当 \(i+hk \sim i,h\in \Z\) 时才会成立),推得\(i \equiv f_i \pmod k\)
类似于循环节部分的证明,不难发现此时条件二为条件三的充分条件,因此我们只需要考虑条件二和条件一的约束。
然后,我们发现卡住了……\(q-1\) 和 \(\lfloor n/2 \rfloor\) 的大小关系会影响 \(f\) 的取值,所以继续暴力分讨!
(由于我本地编辑的视角里,如果下面的两个地方用分数会导致重叠,所以用除法表示。)
-
\(q-1 \le \lfloor n/2 \rfloor\)。此情况下,对于 \(\forall i,f_i \le \lfloor n/2 \rfloor\)。
-
\(q-1 > \lfloor n/2 \rfloor\)。此情况下,对于 \(0\le i \le \lfloor n/2 \rfloor\),类似的,存在 \(f_i=i\)。带入 \(i=1\),能过推得 \(f_i=i(0\le i \le q-1)\)。
先枚举 \(k\) 在枚举 \(p\),然后判断 \(q\) 和 \(\lfloor n/2 \rfloor\) 的大小关系即可。可以通过双指针和逆元预处理达到 \(o(n^2)\)。
Rep(i,n,0) a[i]=i+k>n?lim[i]:min(a[i+k],lim[i]);//make the true lim
int m=0;
while(a[m]>=m) m++;
if(k<=m)//f_0 == 0
{
int num=1;
For(i,1,k-1) modmul(num,(min(a[i],n/2)-i)/k/*f_i=i(mod k)*/+1);
if((k-1)<=n/2) modadd(res,num);
//situation 1: f_0 == 0 && q-1 <= n/2
else modadd(res,1);//situation 2: f_0 == 0 && q-1 > n/2
for(int p=1;p+k<=min(m,n);p++)
{
int q=p+k;
modmul(num,(min(n/2,a[q-1])-(q-1))/k+1);
if(q-1<=n/2) modadd(res,num);//situation 1
else modadd(res,1);//situation 2
if(p<=n/2) modmul(num,inv[(min(n/2,a[p])-p)/k+1]);
}
}
2. $f_0 \neq 0 $
此时,存在 \(2f_0 \sim 0\),类似的,不难推得 \(k|2f_0\)。
-
若 \(k|f_0\),能够推得 $f_0\ge k \Rightarrow k \le \lfloor n/2 \rfloor $。然后加上 \(f_i \equiv i \pmod k\) 即可。直接枚举 \(k\),然后 \(O(n)\) 把每个 \(f_i\) 的取值方案乘起来即可。
int num=min(a[0],n/2)/k; For(i,1,k-1) modmul(num,(min(a[i],n/2)-i)/k+1); modadd(res,num); -
\(k|2f_0\) 但 \(k \nmid f_0\) 时,必然存在 \(2|k\),且 $\frac{k}{2} |f_0 $,不难推得 \(f_0 \equiv \frac{k}{2} \pmod k\)。又有 \(f_0+f_i \sim i\),即 $f_0+f_i \equiv i \pmod k \Rightarrow f_i \equiv i+\frac{k}{2} \pmod k $,代入 \(i=\frac{k}{2}-1\) 时存在 \(f_{\frac{k}{2}-1} \equiv k-1 \pmod k\),由此可得 $f_{\frac{k}{2}-1} \ge k-1 $,又因 \(f_{\frac{k}{2}-1} \ge \lfloor n/2 \rfloor\),得出 $2(k-1) \le n \Rightarrow k-1 \le \lfloor n/2 \rfloor $。
根据条件二,对于所有 \(0 \le i \le \lfloor n/2\rfloor ,f_i+f_i \sim i\),因此 $ 0\le i \le k \le \lfloor n/2\rfloor,f_i \le \lfloor n/2 \rfloor \Rightarrow 0\le i\le n,f_i \le \lfloor n/2 \rfloor $。因此,这部分与上一部分相同,暴力枚举每个位置的可能取值即可。int num=1; For(i,0,k-1) { int l=((k>>1)+i)%k;//min if(a[i]<l||n/2<l) { num=0; break; } modmul(num,(min(a[i],n/2)-l)/k+1); } modadd(res,num);
至此,我们终于讨论完了这道题。总复杂度 \(O(n^2)\),期望得分100。
代码
//I often reminisce about the past
#include<bits/stdc++.h>
using namespace std;
bool Mst;
#define LL long long
#define For(a,b,c) for(int (a)=(b);(a)<=(c);(a)++)
#define Rep(a,b,c) for(int (a)=(b);(a)>=(c);(a)--)
const int N=1e5+10,M=1e6+10,K=30,Mod=998244353;
const int INF=0x3f3f3f3f;
//const LL INF=0x3f3f3f3f3f3f3f3f,all=1ll;
int n,m,k;
template <typename T> void read(T &a){
char c=getchar();T w=1,f=0;while(c<'0'||c>'9'){if(c=='-') w=-w;c=getchar();}
while(c>='0'&&c<='9') f=f*10+c-'0',c=getchar();a=f*w;
}
template <typename T> void tomin(T &a,const T &b){if(b<a) a=b;}
template <typename T> void tomax(T &a,const T &b){if(a<b) a=b;}
void modadd(int &a,const int &b,const int &mod=Mod){a+=b;a<0?a+=Mod:a>=Mod?a-=Mod:0;}
void modsub(int &a,const int &b,const int &mod=Mod){a-=b;a<0?a+=Mod:a>=Mod?a-=Mod:0;}
void modmul(int &a,const int &b,const int &mod=Mod){a=1ll*a*b%mod;}
int ksm(int a,int b,int mod=Mod){int res=1;while(b){if(b&1) modmul(res,a,mod);modmul(a,a,mod);b>>=1;}return res;}
int __inv(int x){return ksm(x,Mod-2,Mod);}
int lim[N];
int a[N];
int inv[N];
bool isi[N];
int getlim(int x)
{
return a[x]+1-x;
}
int Test=1;
void mian()
{
read(n);
For(i,0,n) read(lim[i]);
For(i,2,N-1) inv[i]=__inv(i);inv[1]=1;inv[0]=1;
isi[0]=1;
For(i,1,n)
{
isi[i]=(lim[i]>=i)&isi[i-1];
}
int res=isi[n];
//if f[0]=0;
For(k,1,n)
{
Rep(i,n,0) a[i]=i+k>n?lim[i]:min(a[i+k],lim[i]);//make the true lim
// For(i,0,n) printf("%d ",a[i]);puts("");
int m=0;
while(a[m]>=m) m++;
if(k<=m)//f_0 == 0
{
int num=1;
For(i,1,k-1) modmul(num,(min(a[i],n/2)-i)/k/*f_i=i(mod k)*/+1);
if((k-1)<=n/2) modadd(res,num);//situation 1: f_0 == 0 && q-1 <= n/2
else modadd(res,1);//situation 2: f_0 == 0 && q-1 > n/2
for(int p=1;p+k<=min(m,n);p++)
{
int q=p+k;
modmul(num,(min(n/2,a[q-1])-(q-1))/k+1);
if(q-1<=n/2) modadd(res,num);//situation 1
else modadd(res,1);//situation 2
if(p<=n/2) modmul(num,inv[(min(n/2,a[p])-p)/k+1]);
}
}
// printf("k=%d,res=%d->",k,res);
if(k-1<=n/2)// f_0 != 0
{
if(k<=m)//situation 3: f_0 != 0 && f_0 % k == 0
{
int num=min(a[0],n/2)/k;
For(i,1,k-1) modmul(num,(min(a[i],n/2)-i)/k+1);
modadd(res,num);
}
if(k%2==0)//situation 4:f_0 != 0 && f_0 % k != 0
{
int num=1;
For(i,0,k-1)
{
int l=((k>>1)+i)%k;//min
if(a[i]<l||n/2<l)
{
num=0;
break;
}
modmul(num,(min(a[i],n/2)-l)/k+1);
}
modadd(res,num);
}
}
// printf("%d\n",res);
}
cout<<res;//This is a great question!
}
bool Med;
signed main()
{
cerr<<(&Mst-&Med)/1024.0/1024.0<<" MB\n";
// freopen("equation.in","r",stdin);
// freopen("equation.out","w",stdout);
// cin>>Test;
while(Test--) mian();
return 0;
}

浙公网安备 33010602011771号