D Project Manhattan
思路:
最终选中的下标,必然包含n个行或者n个列.
所以答案 = n行的最小值之和或者n列的最小值之和
注意坑点: 当存在负数时,应该把负数全部选上,答案只会更优.
代码:
- #include <bits/stdc++.h>
- typedef long long ll;
- typedef unsigned long long ull;
- #define int long long
- #define endl '\n'
- #define bit(x) (1ll << x)
- using namespace std;
- const int N = 1e6 + 5;
- const int inf = 1e16;
- void solve()
- {
- int n;
- cin >> n;
- vector<vector<int>> a(n + 1, vector
(n + 1)); - for (int i = 1; i <= n; i++)
- {
- for (int j = 1; j <= n; j++)
- {
- cin >> a[i][j];
- }
- }
-
- int ans1 = 0;
- int ans2 = 0;
- for (int i = 1; i <= n; i++)
- {
- vector<int> c(n + 1);
- for (int j = 1; j <= n; j++)
- {
- c[j] = a[i][j];
- }
- sort(c.begin() + 1, c.end());
- ans1 += c[1];
- int now = 2;
- while (c[now] < 0)
- {
- ans1 += c[now];
- now++;
- }
- }
-
- for (int i = 1; i <= n; i++)
- {
- vector<int> c(n + 1);
- for (int j = 1; j <= n; j++)
- {
- c[j] = a[j][i];
- }
- sort(c.begin() + 1, c.end());
- ans2 += c[1];
- int now = 2;
- while (c[now] < 0)
- {
- ans2 += c[now];
- now++;
- }
- }
- cout << min(ans1,ans2) << endl;
- }
- signed main()
- {
- ios_base::sync_with_stdio(0);
- cin.tie(0);
- cout.tie(0);
- int t = 1;
- cin >> t;
- while (t--)
- {
- solve();
- }
- return 0;
- }
E Another Bus Route Problem
题解:
下面这些话比较抽象, 具体看代码.
记忆化BFS.
对于一条路径(u,v), 不断的找他们的LCA,并标记中途经过的点,如果途中经过的点是线路的话,就加入队列中,然后把步数+1即可.
会有两种情况:
1.整条路径(u,v)都没有被访问过:直接标记即可
2.找LCA的过程中,已经有顶点被标记过了.那么说明他们的LCA也必然被标记过了,否则不可能出现这种状态.没被标记的那个条路径继续往上找.
- #include <bits/stdc++.h>
- typedef long long ll;
- typedef unsigned long long ull;
- #define int long long
- #define endl '\n'
- #define bit(x) (1ll << x)
- using namespace std;
- const int N = 1e6 + 5;
- const int inf = 1e16;
- vector<int> g[N];
- vector<int> fa(N);
- vector<int> dep(N);
- map<int, vector<pair<int, int>>> path;
- vector<int> vis(N); // 标记
- void dfs(int u)
- {
- dep[u] = dep[fa[u]] + 1;
- for (auto v : g[u])
- {
- fa[v] = u;
- dfs(v);
- }
- }
- void solve()
- {
- int n, m;
- cin >> n >> m;
- for (int i = 2; i <= n; i++)
- {
- int x;
- cin >> x;
- g[x].push_back(i);
- }
- dfs(1);//LCA需要得到每个顶点的深度,
- for (int i = 1; i <= m; i++)//把路径存到两个端点上
- {
- int u, v;
- cin >> u >> v;
- path[u].push_back({u, v});
- path[v].push_back({u, v});
- }
- queue<array<int, 3>> q;
- for (int i = 0; i < path[1].size(); i++)//起点肯定是从1号开始的
- {
- q.push({path[1][i].first, path[1][i].second, 1});
- }
- vis[0] = 1e5;//无效状态,直接标记
- while (q.size())
- {
- int left = q.front()[0];
- int right = q.front()[1];
- int step = q.front()[2];//到当前路径(left,right)的转站步数
- q.pop();
- while (left != right && !vis[left] && !vis[right])//找LCA,如果出现已经访问过的点,直接停止
- {
- if (dep[left] < dep[right])
- {
- vis[right] = step;//标记路径
- for (int i = 0; i < path[right].size(); i++)//该点存在路径直接存进去
- {
- q.push({path[right][i].first, path[right][i].second, step + 1});
- }
- right = fa[right];//往上走
- }
- else
- {
- vis[left] = step;
- for (int i = 0; i < path[left].size() ; i++)
- {
- q.push({path[left][i].first, path[left][i].second, step + 1});
- }
- left = fa[left];
- }
- }
- //如果还没到LCA就终止了,说明他们的LCA已经被访问过了.
- while (!vis[left])//如果没被访问过,就一直往上标记,到达LCA或标记过的点就会停止
- {
- vis[left] = step;
- for (int i = 0; i < path[left].size() ; i++)
- {
-
- q.push({path[left][i].first, path[left][i].second, step + 1});
- }
- left = fa[left];
- }
- while (!vis[right])//如果没被访问过,就一直往上找
- {
- vis[right] = step;
- for (int i = 0; i < path[right].size() ; i++)
- {
-
- q.push({path[right][i].first, path[right][i].second, step + 1});
- }
- right = fa[right];
- }
- }
- for (int i = 2; i <= n; i++)
- {
- if (!vis[i])
- {
- cout << -1 << " ";
- continue;
- }
- cout << vis[i] << " ";
- }
- cout << endl;
- }
- signed main()
- {
- ios_base::sync_with_stdio(0);
- cin.tie(0);
- cout.tie(0);
- int t = 1;
- // cin >> t;
- while (t--)
- {
- solve();
- }
- return 0;
- }
M Dirty Work
题解:
写完一道题的期望时间是 ai + bi*pi
把n道题的期望时间从小到大排序,然后按顺序完成题目即可.
- #include <bits/stdc++.h>
- typedef long long ll;
- typedef unsigned long long ull;
- #define int long long
- #define endl '\n'
- #define bit(x) (1ll << x)
- using namespace std;
- const int N = 1e6 + 5;
- const int inf = 1e16;
- void solve()
- {
- int n;
- cin >> n;
- vector<double> ans(n+1);
- for(int i = 1; i<=n; i++)
- {
- double a,b,p;
- cin >> a >> b >> p;
- ans[i] = a + 1.0*b*p;
- }
- sort(ans.begin()+1,ans.end());
- double sum =0;
- for(int i=1; i<=n; i++)
- {
- ans[i]+=ans[i-1];
- sum+=ans[i];
- }
- printf("%.10lf\n",sum);
- }
- signed main()
- {
- int t = 1;
- cin >> t;
- while (t--)
- {
- solve();
- }
- return 0;
- }
I Impatient Patient
题解:
标签:期望方程.
设dp[i]表示从i直接到达终点的期望天数
1. 成功的概率是 pi,直接到达终点
贡献是 :pi*1
2.失败的概率是 1-pi,那么从i点回到a[i]点,然后从a[i]点再到 i号点
贡献是 :(1-pi) * (i- a[i] +1 + dp[i])
综上,得到期望方程
dp[i] = pi + (1-pi) * (i- a[i] +1 + dp[i])
化简得: dp[i] = ( p + (1-p)*(i-a[i]+1) ) /p;
- #include <bits/stdc++.h>
- typedef long long ll;
- typedef unsigned long long ull;
- #define int long long
- #define endl '\n'
- #define bit(x) (1ll << x)
- using namespace std;
- const int N = 1e6 + 5;
- const int inf = 1e16;
-
- void solve()
- {
- int n;
- cin >> n;
- vector<int> a(n + 2), q(n + 2);
- for (int i = 1; i <= n; i++)
- {
- cin >> a[i];
- a[i]++;
- }
- for (int i = 1; i <= n; i++)
- {
- cin >> q[i];
- }
- vector<double> dp(n + 2);
- double ans = n;
- for (int i = 1; i <= n; i++)
- {
- double p = q[i]/(1.0*100000);//成功概率
- dp[i] = ( p + (1-p)*(i-a[i]+1) ) /p;//从i到n+1点的期望天数
- ans = min(ans,i-1+dp[i]);
- }
- printf("%.12lf\n", ans);
- }
- signed main()
- {
- int t = 1;
- cin >> t;
- while (t--)
- {
- solve();
- }
- return 0;
- }
L Super-palindrome
题解:
先陈述一下最小公共前后缀的概念
比如字符串 :abc]defabcabcdef abc
该字符串的最小公共前后缀为3:[abc] defabcabcdef [abc]
字符串: ababababab
该字符串的最小公共前后缀为2: [ab]ababab[ab]
样例字符串: pbbp
该字符串的最小公共前后缀为2: [pb][pb]
设dp[l][r]表示区间[l,r] 是否是超级字符串.
nxt[id][i]表示在字符串 s[id]...s[n]中 以第i个字母为右端点的最小公共前后缀.
转移方程:
dp[l][r] |= dp[l+ Min][r-Min]; //其中 Min表示 字符串s [l,r]的最小公共前后缀.
举例说明:
样例说明:
问字符串[pb]pbpb[pb]是否是超级回文串?
最小公共前后缀[pb]可以成功匹配,所以只要看里面 pbpb 是否是超级回文串即可.
代码:
- #include <bits/stdc++.h>
- typedef long long ll;
- typedef unsigned long long ull;
- #define int long long
- #define endl '\n'
- #define bit(x) (1ll << x)
- using namespace std;
- const int N = 1e6 + 5;
- const int inf = 1e16;
- const int maxn = 5e3 + 10;
- // nxt数组表示 以i为右端点的最短公共前后缀
- int nxt[maxn][maxn];
- void Min_kmp(string a, int id) // 传入的下标一定是从0开始的
- {
-
- a = " " + a;
- int n = a.size() - 1;
- for (int i = 0; i <= n; i++)
- {
- nxt[id][i] = 0;
- }
- for (int i = 2, j = 0; i <= n; i++)
- {
- while (j && a[i] != a[j + 1])
- j = nxt[id][j];
- if (a[i] == a[j + 1])
- j++;
- nxt[id][i] = j;
- }
- for (int i = 2, j = 2; i <= n; i++, j = i)
- {
- while (nxt[id][j])
- j = nxt[id][j];
- if (nxt[id][i])
- nxt[id][i] = j;
- }
- for (int i = 0; i < n; i++)
- {
- nxt[id][i] = nxt[id][i + 1];
- }
- nxt[id][n] = 0;
- }
- int dp[maxn][maxn];
- void solve()
- {
-
- string s;
- cin >> s;
- int n = s.size();
- for(int i = 0; i<=n; i++)
- {
- for(int j = 0; j<=n; j++)
- {
- dp[i][j] = 0;
- }
- }
- for (int i = 0; i < s.size(); i++)
- {
- string t = s.substr(i);
- Min_kmp(t, i);//找到以s[i]为起点字符串的最小公共前后缀
- }
- int ans = 0;
- for (int len = 2; len <= n; len++)
- {
- for (int l = 0; l < n; l++)
- {
- int r = l + len - 1;
- int Min = nxt[l][r - l];//以s[l...r]中以r为右端点的最小公共前后缀
- if (Min * 2 == len)//直接拼接即可
- {
- dp[l][r] |= 1;
- }
- dp[l][r] |= dp[l + Min][r - Min];
- if (dp[l][r])
- {
- ans++;
- }
- }
- }
- cout << ans << endl;
- }
- signed main()
- {
- ios_base::sync_with_stdio(0);
- cin.tie(0);
- cout.tie(0);
- int t = 1;
- cin >> t;
- while (t--)
- {
- solve();
- }
- return 0;
- }