Problem 2. number题解

number:数学+二分图匹配

首先,如果S<N,那么S+1,S+2...N这些数直接放在S+1,S+2...N的位置上(如果其他数x放在这些位置上面,这些数不放在对应位置,那么x一定能放在这些数放的位置,所以直接交换即可)所以可以直接将S和N调换,缩小N。接着看N个连续的数,如果这里面有两个素数,则肯定无解,而在1e9的范围内,素数间隔最大是低于600的,我们就可以通过二分图匹配(s+i与其因数建边)求出最大匹配,若最大匹配为N,则为Yes。实际上,能满足的N其实最大为30多,而菜菜的jyb只枚举到了很多20多的答案,所以为了卡掉暴力匹配的做法(但还是很良心地给了5个点),不得不多设置了很多数据组数。

迷之数学规律,n>600时就会至少有两个素数出现,不符合题意,然后我们一下子就将1e9的数据变成了n<600,之后进行裸的二分图匹配即可,代码如下

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
const int N=605;
bool vis[maxn];
int match[maxn];
int ditu[N][N];
int T,n,s;
bool dfs(int x)//匈牙利算法
{
    if(x==0)
    {
        return false;
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i] && ditu[i][x])
        {
            vis[i]=true;
            if(!match[i]||dfs(match[i]))
            {
                match[i]=x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&s);
        if(s<n)
        {
            swap(s,n);
        }
        if(n>600)
        {
            printf("No\n");
            continue;
        }
        memset(ditu,0,sizeof(ditu));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if((i+s)%j==0)//能够整除的数之间建边,这段代码的灵魂所在
                {
                    ditu[i][j]=1;
                }
            }
        }
        memset(match,0,sizeof(match));
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            memset(vis,0,sizeof(vis));
            if(dfs(i))
            {
                ans++;
            }
        }
        if(ans==n)
        {
            printf("Yes\n");
        }
        else
        {
            printf("No\n");
        }
    }
    return 0;
}

 

posted @ 2019-04-07 14:19  JBLee  阅读(159)  评论(0编辑  收藏  举报