「国庆训练」Kingdom of Obsession(HDU-5943)

题意

给定\(s,n\),把\(s+1,s+2,...,s+n\)\(n\)个数填到\(1,2,...,n\)里,要求\(x\)只能填到\(x\)的因子的位置(即题目中\(x\%y=0\)那么x才能放在y位置的要求),问是否能够把所有数都填上去。

分析

建模之后就是条二分图的板子题。
注意到一个有趣的事实:如果\([s+1,s+n]\)中有两个质数,那他们肯定完蛋。因为它们一定都会和1连边然后翻车。
这意味着什么呢?在\(10^9\)的范围里面,质数最大的间隔是282(我抄网上的结论的,正确不保证,反正不多),那么我们就可以认定如果n比这个数更大一定是No。
然后就可以快乐建图了,把取模为0的建一条有向边,看看最后能否找到完美匹配即可。
实现上,s应当比n大。为什么?如果\(s<n\),那么\([s+1,n]\)直接放在自己的位置上即可,因为不可能放在比他们更大的地方了(集合的交):那爽死了,我们只需要考虑\([1,s]\)与剩下\([n+1,s+n]\)的匹配即可,这个东西的实质就是n、s交换。有没有其他数\(k\)放在这些位置上面(如一个数\(x\))然后这些数字没法放到其他数字的反例呢?没有哦,因为如果\(k\)能放在\([s+1,n]\)的对应位置,那么作为这个数本身的\(x\)一定能放在\(k\)上。所以得到结论:可以直接将\(s\)\(n\)调换来缩小\(n\)

代码

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define ZERO(x) memset((x), 0, sizeof(x))
#define ALL(x) (x).begin(),(x).end()
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define per(i, a, b) for (int i = (a); i >= (b); --i)
#define QUICKIO                  \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
#define MS(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;

const int MAXN=2018;// actually, max distance: 282
bool mat[MAXN][MAXN];
bool is_prime(int x)
{
	if(x<=1) return false;
	else for(ll k=2;k*k<=ll(x);++k)
		 {
			 if(x%k==0) return false;
		 }
	return true;
}

int linker[MAXN];
bool vis[MAXN];

int n;
bool dfs(int x)
{
	rep(i,1,n) if(mat[x][i] && !vis[i])
	{
		vis[i]=true;
                if(linker[i]==-1 || dfs(linker[i]))
		{
			linker[i]=x;
			return true;
		}
	}
	return false;
}

int hungary()
{
	MS(linker,-1);
	int ans=0;
	rep(i,1,n)
	{
		ZERO(vis); // 注意这一行,应当是每一次清空
	 	if(dfs(i)) ans++;
	}
	return ans;
}

int main()
{
	int T; scanf("%d",&T);
	rep(kase, 1, T)
	{
		int primecnt=0;
		int s;
		ZERO(mat);
		scanf("%d%d", &n, &s);
		if(s<n) swap(s,n); // s should be bigger
		if(n>1000)
		{
			printf("Case #%d: No\n", kase);
			continue;
		}
		rep(i,1,n)
		{
			if(is_prime(s+i)) primecnt++;
			if(primecnt>1) break;
		}
		if(primecnt<=1)
		{
			rep(i,1,n)
			{
				int t=s+i;
				rep(j,1,n) if(t%j==0)
					mat[i][j]=true;
			}
			if(hungary()!=n)
				printf("Case #%d: No\n", kase);
			else printf("Case #%d: Yes\n", kase);
		}
		else printf("Case #%d: No\n", kase);
	}
	return 0;
}
posted @ 2018-10-11 00:32  ISoLT  阅读(245)  评论(0编辑  收藏  举报