CF1594 - D~F
D - The Number of Imposters(2-sat)
每個人分成T,F兩種結點,分別代表這人是誠實的還是不誠實的。然後連邊,例如\(a\)說\(b\)是T,那麼就有如果\(a\)是T推出\(b\)是T,於是連一條雙向邊:\(aT \Leftrightarrow bT\),以此類推。然後直接dfs跑,最後判一下有沒有矛盾。
#include <bits/stdc++.h> #define endl '\n' #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0) #define mp make_pair #define seteps(N) fixed << setprecision(N) typedef long long ll; using namespace std; /*-----------------------------------------------------------------*/ ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;} #define INF 0x3f3f3f3f const int N = 1e6 + 10; const double eps = 1e-5; bool vis[N]; bool visid[N]; vector<int> np[N]; int get(int p, int st) { // 1 T 0 F if(st) return 2 * p - 1; return 2 * p; } void add(int u, int v) { np[u].push_back(v); np[v].push_back(u); } int cnt, fcnt; void dfs(int p) { cnt++; fcnt += (p % 2 == 0); visid[(p + 1) / 2] = 1; vis[p] = 1; for(int nt : np[p]) { if(vis[nt]) continue; dfs(nt); } } int main() { IOS; int t; cin >> t; while(t--) { int n, m; cin >> n >> m; for(int i = 1; i <= 2 * n; i++) { vis[i] = 0; visid[i] = 0; np[i].clear(); } while(m--) { int a, b; string c; cin >> a >> b >> c; bool flag = (c == "crewmate"); add(get(a, 1), get(b, flag)); add(get(a, 0), get(b, !flag)); } int ans = 0; for(int i = 1; i <= 2 * n; i++) { if(visid[(i + 1) / 2]) continue; cnt = fcnt = 0; dfs(i); ans += max(cnt - fcnt, fcnt); } bool ok = true; for(int i = 1; i <= n; i++) { if(vis[get(i, 1)] && vis[get(i, 0)]) { ok = false; break; } } if(ok) cout << ans << endl; else cout << -1 << endl; } }
E - Rubik's Cube Coloring (hard version) - (樹上dp)
染色的結點只會影響從它到根結點的路徑上的點,將所有染色結點以及它們到根結點的路徑全部提出來,可以得到一顆樹,點數最多為\(60 \times 2000\)。剩餘的點顯然都是隻有4種染色方式,然後處理樹上的染色方案數。直接樹上dp,設\(dp[p][c]\)代表結點\(p\)染色\(c\)時方案數。有轉移方程
\[dp[p][c]=\sum{dp[u][c_1] \cdot dp[v][c_2]} \]\(u,v\)是\(p\)的兒子,\(c_1,c_2\)是和\(c\)合法的顏色。
注意寫前搞清楚dp轉移方程,在紙上寫下來。不要搞錯了。
#include <bits/stdc++.h> #define endl '\n' #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0) #define mp make_pair #define seteps(N) fixed << setprecision(N) typedef long long ll; using namespace std; /*-----------------------------------------------------------------*/ ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;} #define INF 0x3f3f3f3f const int N = 3e5 + 10; const int M = 1e9 + 7; const double eps = 1e-5; typedef pair<int, int> PII; map<string, int > id; vector<ll> cn[N]; vector<ll> num; vector<int> np[N]; int qcol[N]; int col[N]; ll dp[N][10]; bool vis[N]; inline ll qpow(ll a, ll b, ll m) { ll res = 1; while(b) { if(b & 1) res = (res * a) % m; a = (a * a) % m; b = b >> 1; } return res; } int getid(ll x) { return lower_bound(num.begin(), num.end(), x) - num.begin() + 1; } void cal(int p, int fa, int c) { vector<int> son; for(int nt : np[p]) { if(nt == fa) continue; son.push_back(nt); } if(son.size() == 0) { dp[p][c] = 1; } else if(son.size() == 1) { int u = son.front(); for(int i = 2; i <= 7; i++) { if(c == i || c == (i ^ 1)) continue; dp[p][c] += dp[u][i]; dp[p][c] %= M; } } else { int u = son.front(), v = son.back(); for(int i = 2; i <= 7; i++) { for(int j = 2; j <= 7; j++) { if(c == i || c == (i ^ 1)) continue; if(c == j || c == (j ^ 1)) continue; dp[p][c] += dp[u][i] * dp[v][j] % M; dp[p][c] %= M; } } } } void dfs(int p, int fa) { for(int nt : np[p]) { if(nt == fa) continue; dfs(nt, p); } if(col[p]) { cal(p, fa, col[p]); } else { for(int i = 2; i <= 7; i++) { cal(p, fa, i); } } } int main() { IOS; id["white"] = 2; id["yellow"] = 3; id["green"] = 4; id["blue"] = 5; id["red"] = 6; id["orange"] = 7; int k, n; cin >> k >> n; for(int i = 1; i <= n; i++) { ll v; string c; cin >> v >> c; while(v) { num.push_back(v); cn[i].push_back(v); v >>= 1; } qcol[i] = id[c]; } sort(num.begin(), num.end()); num.erase(unique(num.begin(), num.end()), num.end()); for(int i = 1; i <= n; i++) { int u = getid(cn[i].front()); col[u] = qcol[i]; for(int j = 1; j < cn[i].size(); j++) { int v = getid(cn[i][j]); np[u].push_back(v); np[v].push_back(u); u = v; } } for(int i = 1; i <= num.size(); i++) { sort(np[i].begin(), np[i].end()); np[i].erase(unique(np[i].begin(), np[i].end()), np[i].end()); } dfs(1, 0); ll ans = 1; ll ret = ((1ll << k) - 1) - num.size(); ll res = 0; for(int i = 2; i <= 7; i++) res = (res + dp[1][i]) % M; ans = qpow(4, ret % (M - 1), M) * res % M; cout << ans << endl; }
F - Ideal Farm (思維,構造)
如果\(s < k\),答案是NO
;\(s=k\)答案是YES
。
當\(s>k\)時,設分配為\(a_1,...a_n\),有分配的字首和為\(p_1,...,p_n\),其中\(p_n = s\),\(p_i < p_{i+1}\),\(p_1 \ge 1\)。
這樣轉化後就比較好處理,假設存在某個\(p_i\),有\(p_i +k=p_j\),那麼說明存在一個區間和為\(k\)。因此再建立一個數組\(b\),有\(b_i=p_i+k\),\(b_0=k\)。這樣就有包含\(n\)個元素的\(p\)和包含\(n+1\)個元素的陣列\(b\),一共\(2n+1\)個元素。
顯然,答案為NO
的充要條件就是存在一個分配,使得\(p\)中任意一個元素都不等於\(b\)中任意一個元素,等價於\(p\)和\(b\)構成一個新陣列\(pb\)后里面的元素各不相同。顯然這\(2n+1\)個元素的值域為\([1,s+k]\)。如果能找到\(pb\)的最長分配使得前面的條件成立,設最長的分配長度為\(m\),那麼有\(2n+1\le m\)答案為NO
,否則答案為YES
。因為一旦有最長的分配使得條件成立,讓條件成立的更短的分配一定可以從中構造出來。
注意\(p\)的值域為\([1,s]\),\(b\)的值域為\([k,s+k]\)。最優(長)的分配方案就是\(k\)個一組,一組一組分別分配給\(p\)和\(b\)。例如\([2k,3k-1]\)分配給\(p\),\([3k,4k-1]\)分配給\(b\),這樣即滿足各不相同又滿足了\(b_i=p_i+k\)。可以發現,這樣是成對分配的,所以要看\(\lfloor \frac{s}{k} \rfloor\)是奇數還是偶數,如果是奇數,最後還會剩下一組。最後剩下的從小到大分配即可。注意,\([s+1,s+k]\)這部分的值只能分配給\(b\)。畫一下圖很好理解。這樣就能得到最長的分配長度。
#include <bits/stdc++.h>
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 3e5 + 10;
const double eps = 1e-5;
int main() {
IOS;
int t;
cin >> t;
while(t--) {
ll s, n, k;
cin >> s >> n >> k;
if(s < k) cout << "NO" << endl;
else if(s == k) cout << "YES" << endl;
else {
ll b = s % k + 1;
ll a = k - b;
ll m;
if((s / k) % 2 == 1) {
m = s + k - b;
} else {
m = s + k - a;
}
if(2 * n + 1 <= m) {
cout << "NO" << endl;
} else
cout << "YES" << endl;
}
}
}