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\)时方案数。有转移方程
\(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;
}
}
}