题解:P11748 「TPOI-1B」ASPAP
这题竟然才绿啊,亏我调了3个小时。
其实这个题只需要有两个重要的点,其他都是比较简单的对吧:
- 怎么找到贪心策略。
这个其实也不难,手模一下阳历可以发现,第
思考为什么贪心不成立呢?因为
我们可以分情况讨论,肯定有前一部分排列的最大答案中出现从大到小排序的部分,我们称该排列及之前的部分为不受
诶?这不数位dp嘛(类似)。
嗯,如果只想到这里的话,那么,恭喜你,这是第一步,但是只想到这,还是远远不够的。
理想很丰满,现实很骨感,你看看那个
- 优化,优化,还是优化!
我们这个贪心策略,确实是对的,但是复杂度差到家了。有没有更好的方法呢?
观察到
这个优化已经很好了,但还远远不够
一个顺序序列(假设就是从小到大的),考虑贡献,也无非是
再说说上文说的怎么分前后两个序列呢?先通过遍历,找到 (这个实现真的恶心。)
好了,是时候上代码了。
#include<bits/stdc++.h>
using namespace std;//(这里为了省事直接开 __int128)
#define int __int128
int t,mp[25],vis[25];
const int mod=998244353;
void write(int x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
int dfs(int k,int x,int n,bool f,int S,int sum){
if(x==n){
//write(f);putchar(' ');write(v[0]);putchar(' ');write(v.size());putchar(' ');write(sum+v[0]);puts("");
for(int i=0;i<=n-k;i++){
if(!vis[i])return sum+i+k;
}
}
if(!f){
int ans=0,tot=0;
//for(int i=0;i<len;i++)ans+=(i+1)*v[i];
for(int i=0;i<=n-k;i++){
if(!vis[i]){
ans+=(++tot)*(k+i);
}
}
//write(x);putchar(' ');write(S);putchar(' ');write(ans);putchar(' ');write(sum);putchar(' ');
//puts("");
return sum+ans;
}
int ans=0;
for(int j=0;j<=n-k;j++){
if(vis[j])continue;
int i=j+k;
//for(int j:v)if(i!=j)q.push_back(j);
vis[j]=1;
ans=max(ans,dfs(k,x+1,n,(S-mp[n-x]<=0),S,sum+(n-x+1)*i));
vis[j]=0;
S-=mp[n-x];
if(S<=0)break;
}
return ans;
}
inline int read(){
int x=0;char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c<='9'&&c>='0'){
x=(x<<3)+(x<<1)+c-48;
c=getchar();
}
return x;
}
void solve(){
int n=read(),S=read(),f=1;
//write(S);puts("");
for(int i=n;i>=1;i--){
int j=n-i+1;
if(S<mp[j]){
f=i;break;
}
//write(mp[j]);
}
//write(f);puts("");
int ans=0;
for(int i=f;i<=n;i++){
//vector<int>v;
//for(int j=f;j<=n;j++)if(i!=j)v.push_back(j);
vis[i-f]=1;
ans=max(ans,dfs(f,f+1,n,(S-mp[n-f]<=0),S,(n-f+1)*i));
vis[i-f]=0;
S-=mp[n-f];
if(S<=0)break;
}
int tp1=(n*(n+1)*(2*n+1)/6+n*(n+1)/2)/2;
//write(tp1);puts("");
int tp2=0;
for(int i=f;i<=n;i++){
tp2+=i*(n-i+1);
}
int sum=(ans+tp1-tp2)%mod;
//write(ans);puts("");
write(sum);
puts("");
return ;
}
signed main(){
t=read();
mp[1]=1;
for(int i=2;i<=21;i++){
mp[i]=mp[i-1]*i;
}
while(t--){
solve();
}
}