P9838 [lg noip C] 挑战 NPC IV
Part 1. k=1
首先考虑
考虑最终的位置
也就是说问题转化成,令
这个问题有显然的贪心,
Part 2. n\in [29,10^6]
在上述过程中发现这么一个问题,方案本质不同的定义是
考虑估计这个具体数目,
这说明
这一部分没有单独的部分分,但我们需要加速贪心的过程。
只要快速求出
令
现在这两个序列都能快速计算,我们可以在
Part 4. n\le 28
看起来即使仅考虑本质不同的
考虑基于这个进行 dp。进一步观察,
故设
转移时枚举当前位填什么数,式子是容易写出的。
这样的有效状态数在
结合 Part 3 的做法即可获得 100pts。
#define int long long
const int N=1e6+5,mod=998244353;
int T,n,k;
int f[16][9][5][3][2][11005];
int a[N],b[N],cnt[N],mx=11000,now[6],jc[N];
il int qpow(int n,int k=mod-2)
{
int res=1;
for(;k;n=n*n%mod,k>>=1) if(k&1) res=res*n%mod;
return res;
}
il int S(int x) {x%=mod;return x*(x+1)%mod*(2*x+1)%mod*qpow(6)%mod;}
il int get(int l,int r,int n)
{
if(l>r) return 0;
if(r>n/2&&l<=n/2) return (get(l,n/2,n)+get(n/2+1,r,n))%mod;
if(l>n/2) l=n-l,r=n-r,swap(l,r);
int res=n%mod*((l+r)%mod)%mod*((r-l+1)%mod)%mod*qpow(2)%mod-(S(r)-S(l-1)+mod)%mod;
return (res%mod+mod)%mod;
}
il void solve(int n)
{
int l=1,r=n,res=0;
for(int i=__lg(n)+1;i;i--)
{
int sum=(n>>i)+(n>>(i-1)&1);
int ls=sum/2,rs=sum-ls;
if(l<n-r+1) swap(ls,rs);
(res+=i*get(l,l+ls-1,n+1)%mod)%=mod,(res+=i*get(r-rs+1,r,n+1)%mod)%=mod;
l+=ls,r-=rs;
}
printf("%lld\n",res);
}
signed main()
{
T=read(); jc[0]=1;
for(int i=1;i<=15;i++) jc[i]=jc[i-1]*i;
while(T--)
{
n=read(),k=read();
if(n>28) {solve(n);continue;}
for(int i=1;i<=n;i++) a[i]=1+__lg(i&(-i));
for(int i=1;i<=n;i++) b[i]=i*(n-i+1);
sort(a+1,a+n+1),sort(b+1,b+n+1);
for(int i=1;i<=n;i++) cnt[i]=0;
for(int i=1;i<=n;i++) cnt[a[i]]++;
int Cnt=1;
if(n<=28) for(int i=1;i<=__lg(n)+1;i++) Cnt=Cnt*jc[cnt[i]];
f[0][0][0][0][0][0]=1;
for(now[1]=0;now[1]<=cnt[1];now[1]++)
for(now[2]=0;now[2]<=cnt[2];now[2]++)
for(now[3]=0;now[3]<=cnt[3];now[3]++)
for(now[4]=0;now[4]<=cnt[4];now[4]++)
for(now[5]=0;now[5]<=cnt[5];now[5]++)
{
int sum=0;
for(int i=1;i<=5;i++) sum+=now[i];
if(!sum) continue;
for(int j=0;j<=mx;j++)
{
int i=0;
for(int k=1;k<=5;k++)
{
if(now[k]==0) continue;
if(j-k*b[sum]<0) continue;
now[k]--;
i+=f[now[1]][now[2]][now[3]][now[4]][now[5]][j-k*b[sum]];
now[k]++;
}
f[now[1]][now[2]][now[3]][now[4]][now[5]][j]=i;
}
}
int ans=0;
for(int i=0;i<=mx;i++)
{
ans+=Cnt*f[cnt[1]][cnt[2]][cnt[3]][cnt[4]][cnt[5]][i];
if(ans>=k) {printf("%lld\n",i);break;}
}
}
return 0;
}