D Project Manhattan
思路:
最终选中的下标,必然包含n个行或者n个列.
所以答案 = n行的最小值之和或者n列的最小值之和
注意坑点: 当存在负数时,应该把负数全部选上,答案只会更优.
代码:
#include
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> 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 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 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
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 g[N];
vector fa(N);
vector dep(N);
map>> path;
vector 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> 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
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 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
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 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 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
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;
}