HDU 5943 16年杭州
阿新 • • 發佈:2018-09-19
部分 cto 然而 vector typedef class 思考 || push
(偷偷吐槽網上有些題解沒說清楚為什麽if(n>s) swap(n,s)
題意
給定T組數據,以及n、s \((1 \leq T \leq 100) (1 \leq n \leq 1e9) (0 \leq s \leq 1e9)\)
問是否能得到一個s+1 -> s+n共n個數的排列,使得每個位置i上的值\(v_i\)被i整除
突破口在2e9內兩個相鄰素數間隔不會太大,所以大區間特判,小區間暴力二分圖匹配
然而還要考慮特殊情況,比如當s=0,s=1顯然成立,與n的值無關。
那麽如果s=2呢?同樣顯然成立.理由是
if n<=2, 那麽將3、4填入位置1、2中,
if n > 2, 那麽[1,n]與[s + 1, s + n]將會有重疊,重疊部分直接匹配,剩下兩個相鄰的數必然可以放入1、2中
繼續考慮s=3,你會發現很容易找到滿足題意的情況,但也存在的反例,如當(n+s)%6==1的一些情況
雖然沒有太過直接的規律,但在過程中你會發現如果區間有重疊,那麽相同的數直接匹配後,再在剩余的數中尋找匹配是與我們能找到滿足題意的序列互為充要條件
(因為如果a能放在b位置,b能放在c位置,必然a能放在c位置上,類似這樣去思考
那麽if(n>s) swap(n, s)與原題就是等價的了
而且這樣還能解決我們前面提到的s較小,n很大也可能存在解的問題
總體來說,就是對於出現區間重疊的情況,我們通過交換n,s來縮小區間(事實上只有這塊區間需要我們尋找匹配,這樣才能利用區間間隔特判),然後大區間特判,小區間暴力
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; typedef long long LL; const int maxn = 45000; int T; int n, s; bool is_prime[maxn]; int prime[maxn]; int match[1500]; bool used[1500]; vector<int> G[1500]; int V; void add_edge(int u, int v) { G[u].push_back(v); G[v].push_back(u); } bool dfs(int v) { used[v] = true; for (int i = 0; i < G[v].size(); i++) { int u = G[v][i]; int w = match[u]; if (w < 0 || (!used[w] && dfs(w))) { match[u] = v; match[v] = u; return true; } } return false; } int bi_match() { int res = 0; memset(match, -1, sizeof(match)); for (int v = 0; v < V; v++) { if (match[v] < 0) { memset(used, 0, sizeof(used)); if (dfs(v)) ++res; } } return res; } int main() { scanf("%d", &T); for (int Te = 1; Te <= T; Te ++) { printf("Case #%d: ", Te); scanf("%d%d", &n, &s); if (s <= 2) { puts("Yes"); } else { if (s < n) swap(n, s); if (n <= 500) { V = (n << 1); for (int i = 0; i < V; i++) G[i].clear(); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if ((s + i) % j == 0) { add_edge(n + i - 1, j - 1); } } } int ans = bi_match(); if (ans == n) { puts("Yes"); } else { puts("No"); } } else { puts("No"); } } } return 0; }
HDU 5943 16年杭州