hdu5943——数论知识+二分图匹配

题意

输入n和s(均为1e9),问是否存在一个n的排列p,使得每个i=1~n,(s+i) % p[i] == 0。

思路

数论和二分图匹配居然能结合起来qwq

首先要意识到这实际上是求一个映射,然后要意识到这里有一个争抢性质:一个p[i]只能拥有一个s+i,且一个s+i只能被一个p[i]占领。那么就可以建模为二分图,本题就是在问是否存在一个完美匹配。但是n和s是1e9,真的思路就断了吗?

我们发现一个事情(太菜了发现不了啊QAQ):如果s≥n(从而区间无交)且{ s+i }里面有2个素数,那么它们都只能抢1,那么答案一定是false。那么在s大于n的前提下,我们希望知道1e9范围内最大的素数间距。这玩意大约可以打表?总之这个最大间距不超过1000。那么在s大于n的前提下,n大于1000就返回false。

接下来看s<n的情况:不妨让重叠部分匹配,然后考虑1~s 和 n+1~n+s匹配,即转化为一个n1=s0且s1=n0的子问题(注:在该子问题回答为false时是否还有其他方案使得原问题为true?),这个操作相当于参数交换。这样就转化为上面的情况。

综上,先交换使得s≥n,再跑一个1000点以内的匈牙利板子。

总结:这种范围大了,解已确定,范围不够大则套板的题,在ICPC/CCPC里面似乎有点常见?

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int SZ = 1000 + 5;

int n,s;
bool vis[SZ];int match[SZ];
vector<int> G[SZ];

template<typename Type>inline void read(Type &xx){
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;
}

int dfs(int u){
    for(int v: G[u]){
        if(vis[v]) continue;
        vis[v] = true;
        if(!match[v] || dfs(match[v])){
            match[v] = u;
            return 1;
        }
    }
    return 0;
}

int hungary(int n){
    memset(match,0,sizeof match);
    int ans = 0;
    rep(i,1,n){
        memset(vis,0,sizeof vis);
        ans += dfs(i);
    }
    return ans;
}

int main(int argc, char** argv) {
    int T,cas = 0;read(T);
    while(T--){
        read(n);read(s);
        if(n > s) swap(n,s);
        if(n > 1000){
            printf("Case #%d: No\n",++cas);continue;
        }
        rep(i,1,n) G[i].clear();
        rep(i,1,n)
            rep(j,s+1,s+n)
                if(j % i == 0)
                    G[i].push_back(j-s);
        printf("Case #%d: %s\n",++cas,hungary(n) == n ? "Yes" : "No");
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值