https://www.cnblogs.com/31415926535x/p/11553164.html
感觉很硬核啊这场,,越往后越做不动,,,emmmm,,,(这场是奔着最后一题 2sat 来的,,,上次学这玩意是在今年的3、4月份把,,,早忘得差不多了,,,
A. Yellow Cards
A题较简单,,贪心就行了,,
#include <bits/stdc++.h> #define aaa cout<<233<<endl; #define endl '\n' using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e6 + 5; const int maxm = 2e5 + 233; const int mod = 1e9 + 7; int a[maxn], n; int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); int n, a1, a2, k1, k2; cin >> a1 >> a2 >> k1 >> k2 >> n; int mi = 0, mx = 0; if(a1 * (k1 - 1) + a2 * (k2 - 1) >= n)mi = 0; else mi = n - a1 * (k1 - 1) - a2 * (k2 - 1); if(k1 <= k2) { while(n >= k1 && a1) { --a1; n -= k1; ++mx; } while(n >= k2 && a2) { --a2; n -= k2; ++mx; } } else { while(n >= k2 && a2) { --a2; n -= k2; ++mx; } while(n >= k1 && a1) { --a1; n -= k1; ++mx; } } cout << mi << " " << mx << endl; // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
B. The Number of Products
A掉A题,,感觉还行,,以为后面的都很简单,,然后就在B、C题卡了半天,,
题意就是一个序列中,正区间和负区间的个数有多少,,这里的区间指的是区间积,,,
刚开始以为暴力可过,,(口胡 ,,然后交了一发果断T了,,,
又推了一会发现可以枚举左端点所在的区间,,然后他的贡献就是后面的+2,,+4,,+6等等区间的和的积,,所以只要预处理一下每一个用负点分割的左闭右开的区间的长度,,然后处理成隔一个的后缀和就行了,,,
#include <bits/stdc++.h> #define aaa cout<<233<<endl; #define endl '\n' using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e6 + 5; const int maxm = 2e5 + 233; const int mod = 1e9 + 7; int a[maxn], n; int p[maxn], tot; ll pp[maxn]; int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin >> n; tot = 0; for(int i = 1; i <= n; ++i)cin >> a[i]; for(int i = 1; i <= n; ++i) if(a[i] < 0) p[++tot] = i; p[tot + 1] = n + 1; p[tot + 2] = n + 1; ll ans1 = 0, ans2 = 0; ++tot; for(int i = tot; i >= 1; --i)pp[i] = p[i] - p[i - 1];//--pp[1]; // for(int i = 1; i <= tot; ++i)cout << pp[i] << " ";cout << endl; for(int i = tot - 2; i >= 1; --i)pp[i] += pp[i + 2]; // for(int i = 1; i <= tot; ++i)cout << pp[i] << " ";cout << endl; for(int i = 1; i <= tot; ++i) { // int x = p[i] - p[i - 1] - 1; ll x = pp[i] - pp[i + 2]; ll y = pp[i + 1]; // cout << x << "-" << y << endl; ans1 += x * y; y = pp[i + 2]; ans2 += x * y; --x; while(x)ans2 += x--; } cout << ans1 << " " << ans2 << endl; // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
C. Swap Letters
题意就是两个串,,只含有ab,,然后每一次的操作是挑第一个串中的一个和第二个串中的一个交换,,然后问题最少的操作次数下使得两串一样,,
我当时的思路是用一个 r 表示右端已经修改的位置,,然后遍历一遍,,当第i个位置的不同时,,利用r向后找一个可以交换的,,口胡了一下就直接敲了,,,然后不断的发现逻辑上的bug,,,emmmm,,一直改到成了N方的解法,,,,
看了一下别人的思路,,显然优先考虑相同的两个进行操作,,剩下的也只能操作偶数个,,使用的操作次数就是两次,,一次是反转其中一个,,然后像前面的就行了,,,,奇数个就是无解,,,(会爆ll
#include <bits/stdc++.h> #define aaa cout<<233<<endl; #define endl '\n' using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e6 + 5; const int maxm = 2e5 + 233; const int mod = 1e9 + 7; char s[maxn], t[maxn]; int n; int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin >> n >> s >> t; vector<pair<int, int> > ans; int r = 1; for(int i = 0; i < n; ++i) { if(s[i] != t[i]) { // r = min(r, i + 1); r = i + 1; while(!(t[r] == t[i] && t[r] != s[r]) && r < n)++r; if(r >= n) { r = i + 1; swap(s[i], t[i]); ans.push_back(make_pair(i + 1, i + 1)); // cout << "----" << endl << s << endl << t << endl; while(!(t[r] == t[i] && t[r] != s[r]) && r < n)++r; if(r >= n) { cout << -1 << endl; return 0; } } // cout << i + 1 << r + 1 << endl; ans.push_back(make_pair(i + 1, r + 1)); swap(s[i], t[r]); // cout << s << endl << t << endl; } } cout << ans.size() << endl; for(auto &i: ans)cout << i.first << " " << i.second << endl; // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
D. Ticket Game
博弈?? 先弃了
感觉是一个好多情况的分类讨论题,,,
E. Marbles
状压dp,,,emmmm,,扔了,,
F. Radio Stations
重点来了,,,
这题的题干是真的长,,长篇阅读理解,,,emmmmmm
题目的大意是这样的:一个城市要弄一个发射塔,,它的功率是 \(f (1\le f \le M)\) ,
有 \(p\) 个节目,,对于每一个都有一个需要发射的功率范围: \((L_i, R_i)\) ,,也就是说当 \(f\) 在这个范围时用户才能收到这个节目,,,
然后有一些需求,,数量是n,,,这些需求这样描述: \((a_i, b_i)\) ,,表示对于第 \(i\) 个需求 至少 需要满足一个,,简单说就是至少要挑一个,,,
还有一些节目间的限制条件: \((u, v)\) ,,表示选了 \(u\) 就不能选 \(v\) ,,,反之亦然,,
然后问你这个发射塔的频率 \(f\) 选择多少时使得 \(n\) 个需求都可以满足
如果这题没有 \(f\) 这个限制条件,这题2sat可直接过,,但是不行,,
当然可以枚举 \(f\) ,,从1到M,,但是这样时间复杂度就是n方,,,T穿,,,
只能将 \(f\) 放在我们建立的限制图中,,但是怎么建呢,,,
以前做的2sat题目都是固定的n对物品中每对中选择一个,,其中一些物品有限制条件,,这样做了很多题之后潜移默化的形成了一个固定的模型,,只有这n个物品可以进行操作,,这样的思想也使得我在刚读懂题时即使出现过将 p 个节目分成选或不选这样建图,,但是因为这样不会处理f,,于是放弃这种思路,,,向着将n个需求作为图中的点,,然后有限制的条件间连边,,这样的思考就不得不去枚举f,,,于是只能过小数据,,
这题的解决方法是 在n个节目的后面再加M个可以选择的f的情况 ,,,这样一层点表示选这个情况,,第二层的点表示不选这个点,,建立相应的限制关系,,这样就可以跑一遍2sat得到两个想得到的东西,,,
前面的点的限制条件很好处理,,,按题意搞就行了
接下来处理后面的这M个点的情况,,,如果我们直接枚举每一个f可行解,,,那么和前面的暴力没啥区别,,,一样会T,,,
参考了一个博主的思路
这里我的理解是对于这些可行解都向前连边,,也就是 选f>=l+1必须选f>=l
这样就可以将区间的一个表示的限制条件转化成一个端点的表示,,(感觉有点类似差分的思想,,区间的操作改成端点的操作,,,
这样建图,,跑一边就行了,,,,最后输出答案,,
用tarjan的话判断的条件是两个点间是否不在一个scc中,,然后取最小就行了,,,
用染色法的话需要跑的点只是前半段p个,,,
染色法
栈写反了,,,wa了好几发,,,,(丢人,,,
#include <bits/stdc++.h> #define aaa cout<<233<<endl; #define endl '\n' using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 4e6 + 5; const int maxm = 4e5 + 233; const int mod = 1e9 + 7; int n, p, M, m, all; struct edge { int to, nxt; }edge[maxn << 1]; int tot, head[maxn << 1]; void init() { tot = 0; memset(head, -1, sizeof head); } void addedge(int u, int v) { edge[tot].to = v; edge[tot].nxt = head[u]; head[u] = tot++; } bool vis[maxn]; int sta[maxn], top; bool dfs(int u) { if(vis[u > all ? u - all : u + all])return false; if(vis[u])return true; vis[u] = true; sta[++top] = u; for(int i = head[u]; ~i; i = edge[i].nxt) if(!dfs(edge[i].to)) return false; return true; } bool twosat(int n) { memset(vis, false, sizeof vis); for(int i = 1; i <= n; ++i) { if(vis[i] || vis[i + all])continue; top = -1; if(!dfs(i)) { while(~top)vis[sta[top--]] = false; if(!dfs(i + all))return false; } } return true; } int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin >> n >> p >> M >> m; all = p + M; int u, v; init(); for(int i = 1; i <= n; ++i) { cin >> u >> v; addedge(u + all, v); addedge(v + all, u); } for(int i = 1; i <= p; ++i) { cin >> u >> v; addedge(i, u + p); addedge(u + p + all, i + all); if(v < M)addedge(v + p + 1, i + all), addedge(i, v + p + all + 1); } for(int i = 1; i <= m; ++i) { cin >> u >> v; addedge(u, v + all); addedge(v, u + all); } for(int i = 1; i < M; ++i) { addedge(i + p + 1, i + p); addedge(i + p + all, i + p + 1 + all); // addedge(i + p, i + p + 1); // addedge(i + p + 1 + all, i + p + all); } if(twosat(p)) { vector<int> ans; int f = 0; for(int i = 1; i <= p; ++i) if(vis[i]) ans.push_back(i); for(int i = p + 1; i <= p + M; ++i) if(vis[i]) { f = i - p; } if(!f) { cout << -1 << endl; return 0; } cout << ans.size() << " " << f << endl; for(auto i: ans)cout << i << " "; } else { cout << -1 << endl; } // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
tarjan
#include <bits/stdc++.h> #define aaa cout<<233<<endl; #define endl '\n' using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; // mt19937 rnd(time(0)); const int inf = 0x3f3f3f3f;//1061109567 > 1e9 const ll linf = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 4e6 + 5; const int maxm = 4e5 + 233; const int mod = 1e9 + 7; int n, p, M, m, all; struct edge { int to, nxt; }edge[maxn << 1]; int tot, head[maxn << 1]; void init() { tot = 0; memset(head, -1, sizeof head); } void addedge(int u, int v) { edge[tot].to = v; edge[tot].nxt = head[u]; head[u] = tot++; } int low[maxn], dfn[maxn], sta[maxn], belong[maxn]; int idx, top; int scc; bool insta[maxn]; int num[maxn]; void tarjan(int u) { int v; low[u] = dfn[u] = ++idx; sta[top++] = u; insta[u] = true; for(int i = head[u]; ~i; i = edge[i].nxt) { v = edge[i].to; if(!dfn[v]) { tarjan(v); if(low[u] > low[v])low[u] = low[v]; } else if(insta[v] && low[u] > dfn[v]) low[u] = dfn[v]; } if(low[u] == dfn[u]) { ++scc; do { v = sta[--top]; insta[v] = false; belong[v] = scc; ++num[scc]; } while(v != u); } } bool twosat(int n) { memset(dfn, 0, sizeof dfn); memset(insta, false, sizeof insta); memset(num, 0, sizeof num); idx = scc = top = 0; for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i); vector<int> ans; int f = 0; // for(int i = 1; i <= p; ++i)cout << belong[i] << " ";cout << " "; // for(int i = 1; i <= M; ++i)cout << belong[i + p] << " ";cout << endl; // for(int i = 1; i <= p; ++i)cout << belong[i + all] << " ";cout << " "; // for(int i = 1; i <= M; ++i)cout << belong[i + p + all] << " ";cout << endl; for(int i = 1; i <= p; ++i) if(belong[i] < belong[i + all]) ans.push_back(i); else if(belong[i] == belong[i + all]) return false; for(int i = 1; i <= M; ++i) if(belong[i + p] < belong[i + p + all]) f = i; else if(belong[i + p] == belong[i + p + all]) return false; cout << ans.size() << " " << f << endl; for(auto i: ans)cout << i << " "; return true; } int main() { // double pp = clock(); // freopen("233.in", "r", stdin); // freopen("233.out", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin >> n >> p >> M >> m; all = p + M; int u, v; init(); for(int i = 1; i <= n; ++i) { cin >> u >> v; addedge(u + all, v); addedge(v + all, u); } for(int i = 1; i <= p; ++i) { cin >> u >> v; addedge(i, u + p); addedge(u + p + all, i + all); if(v < M)addedge(v + p + 1, i + all), addedge(i, v + p + all + 1); } for(int i = 1; i <= m; ++i) { cin >> u >> v; addedge(u, v + all); addedge(v, u + all); } for(int i = 1; i < M; ++i) { addedge(i + p + 1, i + p); addedge(i + p + all, i + p + 1 + all); // addedge(i + p, i + p + 1); // addedge(i + p + 1 + all, i + p + all); } if(!twosat(all << 1))cout << -1 << endl; // cout << endl << (clock() - pp) / CLOCKS_PER_SEC << endl; return 0; }
这道题的启发很大,,,2sat不一定就是解决一种选择下的解,,,只要这些不同种类的物品间有限制条件就可以放在一起,,,
(end)