P7519 [省选联考 2021 A/B 卷] 滚榜

考虑按照顺序分配 \(b\),如果分配的 \(b\) 之和不足 \(m\) ,可以把剩下的题目全部给最后一个人,所以对于一个排列,只用考虑祂 \(b\) 之和最小的分配方案。
先考虑一个暴力,设 \(f_{s,i,j,k}\) 表示已分配 \(b\) 的集合为 \(s\) 上一个分配的人为 \(j\),封榜后总共通过了 \(k\) 题的方案数。
考虑优化,由于 \(b\) 是不降的,所以当给 \(b_i\) 加上 \(x\) 时,后面的所有 \(b\) 都的加上 \(x\),相对大小不变,所以可以贡献提前算。最后注意一下第一个 \(b\) 必须比最大的那个数大和相同题目时按编号排序。

代码

/*
Luogu P7519 [省选联考 2021 A/B 卷] 滚榜
2026-03-05
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define ll long long
const int maxn=20,maxm=510;
int n,m,a[maxn],f[(1<<13)][maxn][maxm],id;
signed main(){
    read(n,m);
    for(int i=1;i<=n;i++) read(a[i]),id=a[i]>a[id]?i:id;
    f[0][0][0]=1;
    for(int i=0;i<(1<<n);i++) for(int j=0;j<=n;j++){
        if(j&&!(i&(1<<j-1))) continue;
        int g=0;
        for(int k=1;k<=n;k++) g+=!!(i&(1<<k-1));
        g=n-g;
        for(int nt=1;nt<=n;nt++){
            if(i&(1<<nt-1)) continue;
            int gx;
            if(a[nt]>a[j]) gx=0;
            else if(a[nt]<=a[j]) gx=a[j]-a[nt]+(nt>j);
            if(j==0) gx=a[id]-a[nt]+(nt>id);
            for(int k=0;k<=m;k++){
                if(k+g*gx>m) break;
                f[i^(1<<nt-1)][nt][k+g*gx]+=f[i][j][k];
            }
        }
    }
    ll ans=0;
    for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) ans+=f[(1<<n)-1][i][j];
    write(ans);
    return 0;
}
posted @ 2026-03-06 20:42  Link-Cut_Trees  阅读(3)  评论(0)    收藏  举报