[Code+#4] 组合数问题2

Luogu4370

一开始入队的数字肯定是\(C^{n/2}_n\),然后将它上下左右能入堆的入堆,取出堆首元素后以此类推

要注意同一个组合数不能重复进堆,所以需要判重,但是如果根据当前扩展的元素是否在答案序列中来判断是否应该进堆,比如这样:

void bfs(int n,int k) {
    const int dx[4]= {1,-1,0,0},dy[4]= {0,0,1,-1};
    q.push(Comb(n,n/2));
    while(!q.empty()) {
        Comb u=q.top();q.pop();
        ans.insert(make_pair(u.n,u.m));
        if(ans.size()==k) return;
        for(int i=0; i<4; i++) {
            int a=u.n+dx[i],b=u.m+dy[i];
            if(a>=0 && a<=n && b>=0 && b<=a && !ans.count(make_pair(a,b))) {//这里用是否在答案序列中来判断是否进堆
                q.push(Comb(a,b));
            }
        }
    }
}

这样还是会有很多重复元素进堆,还是会超时
因为如果当前扩展到了\(C_n^m\) ,而\(C_n^m\) 不在答案序列中,但是它有可能已经在堆中了!只是还没有弹出!所以\(C_n^m\)就会被再次压入堆中!
输出中间结果可以发现,这样的话同一个元素远不止会进堆两次,造成了大量的重复

所以要根据"是否已经进过堆"来判重.
再开一个vis来记录就行了

#include <set>
#include <cmath>
#include <queue>
#include <cstdio>
#include <iostream>
using namespace std;
typedef pair<int,int> pii;
const int MAXN=1e6+10,mod=1e9+7;
int f[MAXN],inv[MAXN],g[MAXN];
double ln_fact[MAXN];
struct Comb {
    int n,m;
    Comb(int a=0,int b=0):n(a),m(b) {}
    bool operator < (const Comb& rhs)const {
        return ln_fact[n]-ln_fact[m]-ln_fact[n-m] < ln_fact[rhs.n]-ln_fact[rhs.m]-ln_fact[rhs.n-rhs.m];
    }
};
priority_queue<Comb>q;
set<pii>ans,vis;
inline int pow(int base,int index) {
    int ans=1;
    while(index) {
        if(index&1) ans=(long long)ans*base%mod;
        base=(long long)base*base%mod;
        index>>=1;
    }
    return ans;
}
inline void init(int maxn) {
    ln_fact[1]=0;f[0]=1;inv[1]=1;g[0]=1;
    for(int i=2; i<=maxn; i++) ln_fact[i]=ln_fact[i-1]+log(i);
    for(int i=1; i<=maxn; i++) f[i]=(long long)f[i-1]*i%mod;
    for(int i=2; i<=maxn; i++) inv[i]=(long long)(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1; i<=maxn; i++) g[i]=(long long)g[i-1]*inv[i]%mod;
}
void bfs(int n,int k) {
    const int dx[4]= {1,-1,0,0},dy[4]= {0,0,1,-1};
    q.push(Comb(n,n/2));
    while(!q.empty()) {
        Comb u=q.top();q.pop();
        ans.insert(make_pair(u.n,u.m));
        if(ans.size()==k) return;
        for(int i=0; i<4; i++) {
            int a=u.n+dx[i],b=u.m+dy[i];
            if(a>=0 && a<=n && b>=0 && b<=a && !vis.count(make_pair(a,b))) {
                vis.insert(make_pair(a,b));
                q.push(Comb(a,b));
            }
        }
    }
}
int main() {
    int n,k;
    scanf("%d %d",&n,&k);
    init(n);
    bfs(n,k);
    long long sum=0;
    for(set<pii>::iterator it=ans.begin(); it!=ans.end(); it++) {
        int a=(*it).first,b=(*it).second;
        sum=(sum+(long long)f[a]*g[b]%mod*g[a-b]%mod)%mod;
    }
    cout<<sum;
    return 0;
}

压行技术大有提升qaq

猜你喜欢

转载自www.cnblogs.com/zhinv/p/9819046.html