2023/8/9~2023/8/11 做题
- 2023/8/9~2023/8/11 做题
- Codeforces Round 121 (Div. 1) C. Fools and Roads
- Codeforces Round 343 (Div. 2) C. Famil Door and Brackets
- Codeforces Round 880 (Div. 1) A. k-th equality
- Codeforces Round 867 (Div. 3) G1. Magic Triples (Easy Version)
- Codeforces Round 567 (Div. 2)D. Irrigation
- Mail.Ru Cup 2018 Round 3 E. Check Transcription
- RCC 2014 Warmup (Div. 2) D. Cunning Gena
- Codeforces Round 867 (Div. 3) F. Gardening Friends
- Codeforces Round 296 (Div. 2) D. Clique Problem
- Codeforces Beta Round 52 (Div. 2) E. Domino Principle
- Codeforces Round 212 (Div. 2) C. Insertion Sort
- VK Cup 2015 - Round 2 (unofficial online mirror, Div. 1 only) B. Work Group
- Codeforces Round 874 (Div. 3) F. Ira and Flamenco
- Codeforces Round 455 (Div. 2) D - Colorful Points
- Codeforces Round 870 (Div. 2) D. Running Miles
- Codeforces Round 179 (Div. 2) B. Yaroslav and Two Strings
Codeforces Round 121 (Div. 1) C. Fools and Roads
树形dp + LCA
先预处理LCA,将边下放到点处理, 对于每个路径在其 \(u\) , \(v\), \(lca\) 处分别打个标记,树形dp合并即可
const int N = 2e5 + 10;
const int LOGN = 20;
int n;
vector<int> e[N];
int id[N], cnt, dep[N], fa[N][LOGN + 2], val[N];
int res[N];
map<pair<int, int>, int> mp;
void lca_init()
{
for(int j = 1; j <= LOGN; j++)
for(int i = 1; i <= n; i++)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
int lca_query(int u, int v)
{
if(dep[u] < dep[v]) swap(u, v);
int d = dep[u] - dep[v];
for(int j = LOGN; j >= 0; j--)
if(d & (1 << j))
u = fa[u][j];
if(u == v) return u;
for(int j = LOGN; j >= 0; j--)
if(fa[u][j] != fa[v][j])
u = fa[u][j],v = fa[v][j];
return fa[u][0];
}
void dfs(int u, int from)
{
dep[u] += dep[from] + 1;
for(auto v : e[u])
{
if(v == from) continue;
id[v] = mp[{u, v}];
fa[v][0] = u;
dfs(v, u);
}
}
void dfs2(int u, int from)
{
for(auto v : e[u])
{
if(v == from) continue;
dfs2(v, u);
val[u] = val[u] + val[v];
}
res[id[u]] = val[u];
}
void solve()
{
cin>>n;
for(int i = 1; i < n; i++)
{
int u, v; cin>>u>>v;
mp[{u, v}] = ++cnt;
mp[{v, u}] = cnt;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
lca_init();
int q; cin>>q;
while(q--)
{
int u, v; cin>>u>>v;
int lca = lca_query(u, v);
val[u]++, val[v]++, val[lca] -= 2;
}
// for(int i = 1; i <= n; i++)
// cout<<id[i]<<" ";
// cout<<'\n';
// for(int i = 1; i <= n; i++)
// cout<<val[i]<<" ";
// cout<<'\n';
dfs2(1, 0);
for(int i = 1; i < n; i++)
cout<<res[i]<<" ";
cout<<'\n';
return;
}
Codeforces Round 343 (Div. 2) C. Famil Door and Brackets
dp
定义状态 \(f_{i,j}\) ,为有 \(i\) 长度的字符串,其 \(j\) 为 \((\) 的数量 - \()\) 的数量,其中找出 \(S\) 串中 \((\) 的数量 - \()\) 的最小值 \(miv\), 其 \(P\) 的\((\) 的数量 - \()\) 的数量 \(\geq miv\),其 \(Q\) 中受到 \(P\) 和 \(S\) 的限制,\((\) 的数量 \(\geq )\) 的数量,我们只要倒着算答案就行
其中满足 \(i + \texttt{S 中 前缀'('数量 - 前缀')' 的最小值} \geq 0 \&\& i + \texttt{S 中 '('数量 - ')' 的数量} \leq n - m - len\) 的条件
int n, m;
int f[N][N], g[N][N];
string s;
void solve()
{
cin>>n>>m>>s;
int cnt = 0, miv = 1e9;
for(auto it : s)
{
if(it == '(') cnt++;
else cnt--;
miv = min(cnt, miv);
}
f[0][0] = 1;
for(int i = 1; i <= N - 10; i++)
{
f[i][0] = f[i - 1][1];
for(int j = 1; j <= i; j++)
f[i][j] = (f[i - 1][j - 1] + f[i - 1][j + 1]) % mod;
}
ll res = 0;
for(int len = 0; len <= n - m; len++)
{
for(int i = 0; i <= len; i++)
{
if(i + miv >= 0 && i + cnt <= n - m - len)
{
//cout<<len<<" "<<i<<" "<<f[len][i]<<" "<<f[n - m - len][i + cnt]<<'\n';
res = (res + 1ll * f[len][i] * f[n - m - len][i + cnt]) % mod;
}
}
}
cout<<res<<'\n';
return;
}
Codeforces Round 880 (Div. 1) A. k-th equality
在数位要求下,找到等式的第 \(k\) 小字典序
直接跑暴力
注意到题面 Each input file has at most $ 5 $ test cases which do not satisfy $ A, B, C \leq 3 $ .就是我们可以跑暴力的依据
穷举数字 \(a\) ,根据 \(c\) 的上下界确定 \(b\) 的上下界即可
ll qpow(ll base, ll r)
{
int res = 1;
while(r--)
res = res * base;
return res;
}
void solve()
{
ll a, b, c, k; cin>>a>>b>>c>>k;
ll down1 = qpow(10, a - 1), up1 = qpow(10, a) - 1;
ll down2 = qpow(10, b - 1), up2 = qpow(10, b) - 1;
ll down3 = qpow(10, c - 1), up3 = qpow(10, c) - 1;
ll d = 0;
for(ll i = down1; i <= up1; i++)
{
ll miv = down3 - i;
ll mav = up3 - i;
ll l1 = max(miv, down2);
ll r1 = min(mav, up2);
d += max(0ll, (r1 - l1 + 1));
if(d >= k)
{
d -= (r1 - l1 + 1);
d = k - d;
cout<<i<<" + "<<l1 + d - 1<<" = "<<i + l1 + d - 1<<'\n';
return;
}
}
cout<<-1<<'\n';
return;
}
Codeforces Round 867 (Div. 3) G1. Magic Triples (Easy Version)
对于一个数,他的上 \(a \times b \times b \leq 1000000\) 其 \(1 \leq b \leq 1000\) 次
对于\(1 \leq i \leq 200000\) 对于每个 \(i\) 去做枚举 \(b\) 这个事情, 总共可以枚举到的 \(b\) 的数量只有 \(793494\) 个,所以枚举就行
ll n, f[N];
void solve()
{
cin>>n;
vector<ll> a;
for(int i = 1; i <= n; i++)
{
ll x; cin>>x;
a.push_back(x);
f[x]++;
}
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end());
ll res = 0;
for(auto it : a)
{
for(int j = 1; it * j * j <= 1000000; j++)
{
if(j == 1 && f[it] >= 3)
res = res + f[it] * (f[it] - 1) * (f[it] - 2);
else if(j != 1)
res = res + f[it] * f[it * j] * f[it * j * j];
}
f[it] = 0;
}
cout<<res<<'\n';
return;
}
Codeforces Round 567 (Div. 2)D. Irrigation
考虑离线,线段树维护最小编号城市,线段树二分查找答案
先将最小举办次数的城市编号在线段树上赋值,每次给最小举办城市编号的举办次数 + 1,同时若有询问 \(k \leq \texttt{历史举办次数 + 最小举办城市数量}\),就在线段树上二分查找,然后将新成为最小举办城市编号的城市在线段树上赋值。
为什么这样模拟可以,前 \(n\) 年的数量很少,这样最多只有\(\sqrt{n}\) 次模拟,所有城市的举办次数就都一样。然后直接取模算答案
具体见代码
const int N = 5e5 + 10;
ll n, m, k;
ll a[N], res[N];
vector<ll> vx;
struct segtree
{
int val;
}seg[N * 4];
void update(int id)
{
seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}
void change(int id, int l, int r, int pos)
{
//cout<<l<<'\n';
if(l == r)
{
seg[id].val = 1;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)
change(id * 2, l, mid, pos);
else
change(id * 2 + 1, mid + 1, r, pos);
update(id);
}
int query(int id, int l, int r, int k)
{
if(l == r)
{
return l;
}
int mid = (l + r) >> 1;
if(seg[id * 2].val >= k)
return query(id * 2, l, mid, k);
else
return query(id * 2 + 1, mid + 1, r, k - seg[id * 2].val);
}
void solve()
{
cin>>n>>m>>k;
for(int i = 1; i <= n; i++)
{
ll x; cin>>x;
a[x]++;
}
vector<pair<ll, ll>> b, event;
vector<int> c;
ll miv = 1e18;
for(int i = 1; i <= k; i++)
{
ll x; cin>>x; x-= n;
event.push_back({x, i});
}
for(int i = 1; i <= m; i++)
{
if(a[i] < miv)
{
c.clear();
miv = a[i];
}
if(a[i] == miv)
c.push_back(i);
}
for(int i = 1; i <= m; i++)
if(a[i] != miv)
b.push_back({a[i], i});
sort(b.begin(), b.end());
sort(event.begin(), event.end());
for(auto it : c)
change(1, 1, m, it);
ll last = 0, cnt = 0, cnt2 = 0;
while(cnt2 < b.size() && cnt < k)
{
//cout<<seg[1].val<<" "<<cnt<<" "<<cnt2<< '\n';
ll add = c.size();
while(cnt < k && last + add >= event[cnt].first)
{
res[event[cnt].second] = query(1, 1, m, event[cnt].first - last);
cnt++;
}
last += add;
miv++;
while(cnt2 < b.size() && b[cnt2].first == miv)
{
change(1, 1, m, b[cnt2].second);
c.push_back(b[cnt2].second);
cnt2++;
}
//cout<<seg[1].val<<'\n';
//cout<<cnt<<" "<<cnt2<<'\n';
}
for(; cnt < k; cnt++)
{
//res[event[cnt].second] = (event[cnt].first - last) % m;
if((event[cnt].first - last) % m == 0)
res[event[cnt].second] = m;
else
res[event[cnt].second] = (event[cnt].first - last) % m;
}
for(int i = 1; i <= k; i++)
cout<<res[i]<<'\n';
return;
}
Mail.Ru Cup 2018 Round 3 E. Check Transcription
哈希
哈希处理 \(t\) ,直接枚举\(r_0\)的长度,可得出 \(r_1\)的长度,根据其长度哈希判断即可
为什么不会 \(TLE\) 呢?
见此处 题解 CF1056E 【Check Transcription】
注意特判
typedef pair<long long, long long> pll;
struct DoubleStringHash
{
vector<long long> h1, h2, w1, w2;
long long base1 = 131, base2 = 13331;
long long p1 = 1e9 + 7, p2 = 1e9 + 9;
void init(string s) {
int len = s.size();
s = " " + s;
h1.resize(len + 1), w1.resize(len + 1);
h2.resize(len + 1), w2.resize(len + 1);
h1[0] = 0, w1[0] = 1;
h2[0] = 0, w2[0] = 1;
for(int i = 1; i <= len; i++) {
h1[i] = (h1[i - 1] * base1 + s[i]) % p1, w1[i] = w1[i - 1] * base1 % p1;
h2[i] = (h2[i - 1] * base2 + s[i]) % p2, w2[i] = w2[i - 1] * base2 % p2;
}
}
pll get(int l, int r) {
return {(h1[r] - h1[l - 1] * w1[r - l + 1] % p1 + p1) % p1, (h2[r] - h2[l - 1] * w2[r - l + 1] % p2 + p2) % p2};
}
bool same(pll a, pll b)
{
if(a.first == b.first && a.second == b.second)
return true;
else
return false;
}
};
string t, s;
int z, o;
void solve()
{
cin>>t>>s;
DoubleStringHash ha;
ha.init(s);
int n = s.size();
s = "?" + s;
int res = 0;
if(t[0] == '1')
for(auto &it : t)
{
if(it == '0') it = '1';
else it = '0';
}
for(auto it : t)
if(it == '0') z++;
else o++;
for(int len = 1; len <= n; len++)
{
int l = 1;
pll p0 = {-1, -1}, p1 = {-1, -1};
if(1ll * z * len > n) break;
int len0 = z * len;
int len1 = n - len0;
if(len1 % o != 0 || len1 <= 0) continue;
int le1 = len1 / o;
for(auto it : t)
{
if(it == '0')
{
if(p0.first == -1)
p0 = ha.get(l, l + len - 1);
else if(!ha.same(p0, ha.get(l, l + len - 1)))
continue;
l = l + len;
}
else
{
if(p1.first == -1)
p1 = ha.get(l, l + le1 - 1);
else if(!ha.same(p1, ha.get(l, l + le1 - 1)))
continue;
l = l + le1;
}
//cout<<len<<" "<<le1<<" "<<l<<"\n";
}
if(!ha.same(p0, p1) && l == n + 1)
{
//cout<<len<<" p0 & p1"<<p0.first<<" "<<p0.second<<" "<<p1.first<<" "<<p1.second<<'\n';
res++;
}
}
cout<<res<<'\n';
return;
}
RCC 2014 Warmup (Div. 2) D. Cunning Gena
状压dp,直接定义 \(f[n][1 << m]\) 这么大的数组会 MLE , 那么我们就用滚动数组就好了
先预处理出每个人的可以解决问题的二进制状态 \(st\),再进行已显示器数量 \(k\) 为关键字进行排序
转移方程 \(f_{i \& 1, S}\) 表示已经选到第 \(i\) 个人, 解决了问题的二进制状态为 \(S\) 的最小花费
const int N = 1e2 + 10;
const int M = 20;
ll f[2][1 << M];
ll n, m, b;
array<ll, 3> a[N];
void solve()
{
cin>>n>>m>>b;
for(int i = 1; i <= n; i++)
{
ll x, y, z, s = 0; cin>>x>>y>>z;
for(int j = 0; j < z; j++)
{
int t; cin>>t; t--;
s = s + (1 << t);
}
a[i] = {y, x, s};
}
sort(a + 1, a + 1 + n);
for(int i = 0; i < 1 << M; i++)
for(int j = 0; j <= 1; j++)
f[j][i] = 3e18;
f[1][0] = 0;
// for(int i = 1; i <= n; i++)
// cout<<a[i][0]<<" "<<a[i][1]<<" "<<a[i][2]<<'\n';
for(int i = 1; i <= n; i++)
{
for(int S = 0; S < (1 << m); S++)
{
f[(i + 1) & 1][S] = min(f[i & 1][S], f[(i + 1) & 1][S]);
int st = S | a[i][2];
if(st == (1 << m) - 1)
f[(i + 1) & 1][st] = min(f[i & 1][S] + a[i][1] + a[i][0] * b, f[(i + 1) & 1][st]);
else
f[(i + 1) & 1][st] = min(f[i & 1][S] + a[i][1], f[(i + 1) & 1][st]);
//cout<<i<<" "<<S<<" "<<st<<" "<<f[(i + 1) & 1][st]<<'\n';
}
}
if(f[(n + 1) & 1][(1 << m) - 1] >= 3e18) f[(n + 1) & 1][(1 << m) - 1] = -1;
cout<<f[(n + 1) & 1][(1 << m) - 1]<<'\n';
return;
}
Codeforces Round 867 (Div. 3) F. Gardening Friends
很板子的换根dp,第一遍dfs预处理出最长链,次长链,和深度,第二遍dfs计算来自父亲的最长链,这里特判如果对最长链的儿子转移,那么去父亲和次长链的最大值 + 1进行转移
const int N = 2e5 + 10;
int dep[N];
ll n, k, c;
ll f[N][2][2], g[N];
// 0最大,1次大
vector<int> e[N];
void dfs(int u, int from)
{
dep[u] = dep[from] + 1;
f[u][0][0] = 0, f[u][0][1] = u;
for(auto v : e[u])
{
if(v == from) continue;
dfs(v, u);
if(f[v][0][0] + 1 > f[u][0][0])
{
f[u][1][0] = f[u][0][0];
f[u][1][1] = f[u][0][1];
f[u][0][0] = f[v][0][0] + 1;
f[u][0][1] = v;
}
else if(f[v][0][0] + 1 > f[u][1][0])
{
f[u][1][0] = f[v][0][0] + 1;
f[u][1][1] = v;
}
}
}
void dfs2(int u, int from)
{
for(auto v : e[u])
{
if(v == from) continue;
if(f[u][0][1] == v)
g[v] = max(g[u] + 1, f[u][1][0] + 1);
else
g[v] = max(g[u] + 1, f[u][0][0] + 1);
dfs2(v, u);
}
}
void solve()
{
cin>>n>>k>>c;
for(int i = 1; i <= n; i++)
{
dep[i] = f[i][0][0] = f[i][0][1] = f[i][1][0] = f[i][1][1] = g[i] = 0;
e[i].clear();
}
for(int i = 1; i < n; i++)
{
int u, v; cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
dfs2(1, 0);
ll res = 0;
for(int i = 1; i <= n; i++)
res = max(-(dep[i] - dep[1]) * c + max(f[i][0][0], g[i]) * k, res);
// for(int i = 1; i <= n; i++)
// {
// cout<<f[i][0][0]<<" "<<f[i][0][1]<<" "<<f[i][1][0]<<" "<<f[i][1][1]<<" "<<g[i]<<'\n';
// }
// cout<<"-----\n";
cout<<res<<'\n';
return;
}
Codeforces Round 296 (Div. 2) D. Clique Problem
离散化,线段树维护区间最值 ,dp
考虑从左往右dp,一个点 \(i\) 的影响范围是 \(x_i + w_i\), 那么这个点对范围 \([-\infty,x_i + w_i]\)的贡献是 \([-\infty, x_i - w_i]\) 的区间最大值 + 1,我们将所有的点存进来,离散化,按\(x_i + w_i\) 为第一关键字, \(x_i - w_i\) 为第二关键字进行排序,然后不断进行以上过程即可
时间复杂度\(O(N \log N)\)
但其实可以发现可以按照线段覆盖的模型来写,就不用什么线段树了P1803 凌乱的yyy / 线段覆盖
const int N = 2e6 + 10;
int n;
array<ll, 2> a[N];
vector<ll> vx;
struct segtree
{
int val;
}seg[N * 4];
void update(int id)
{
seg[id].val = max(seg[id * 2].val, seg[id * 2 + 1].val);
}
void change(int id, int l, int r, int pos, int tag)
{
if(l == r)
{
seg[id].val = max(seg[id].val, tag);
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)
change(id * 2, l, mid, pos, tag);
else
change(id * 2 + 1, mid + 1, r, pos ,tag);
update(id);
}
int query(int id, int l, int r, int ql, int qr)
{
if(ql == l && qr == r)
{
return seg[id].val;
}
int mid = (l + r) >> 1;
if(qr <= mid)
return query(id * 2, l, mid, ql, qr);
else if(ql > mid)
return query(id * 2 + 1, mid + 1, r, ql, qr);
else
return max(query(id * 2, l, mid, ql, mid), query(id * 2 + 1, mid + 1, r, mid + 1, qr));
}
int get_id(ll t)
{
return lower_bound(vx.begin(), vx.end(), t) - vx.begin() + 1;
}
void solve()
{
cin>>n;
for(int i = 1; i <= n; i++)
{
ll x, w; cin>>x>>w;
a[i] = {x + w, x - w};
vx.push_back(x - w);
vx.push_back(x);
vx.push_back(x + w);
}
sort(vx.begin(), vx.end());
vx.erase(unique(vx.begin(), vx.end()), vx.end());
int m = vx.size(), res = 0;
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; i++)
{
int id = get_id(a[i][1]);
int add = query(1, 1, m, 1, id);
id = get_id(a[i][0]);
change(1, 1, m, id, add + 1);
res = max(add + 1, res);
}
cout<<res<<'\n';
return;
}
Codeforces Beta Round 52 (Div. 2) E. Domino Principle
线段树,离散化
观察得,倒向右边会压到骨牌,这个骨牌倒下又会压到其他的骨牌
所以我们肯定要倒着做,按坐标排序,用线段树维护倒下后最大的距离,维护区间中的骨牌数量
没看到坐标可以是负数,wa了几发
const int N = 3e5 + 10;
int n, pos[N], res[N];
array<int, 3> a[N];
vector<int> vx;
struct segtree
{
int mav, val;
}seg[N * 4];
void update(int id)
{
seg[id].mav = max(seg[id * 2].mav, seg[id * 2 + 1].mav);
seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}
void change(int id, int l, int r, int pos, int tag)
{
if(l == r)
{
seg[id].mav = max(seg[id].mav, tag);
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)
change(id * 2, l, mid, pos, tag);
else
change(id * 2 + 1, mid + 1, r, pos ,tag);
update(id);
}
void change2(int id, int l, int r, int pos, int tag)
{
if(l == r)
{
seg[id].val = seg[id].val + tag;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)
change2(id * 2, l, mid, pos, tag);
else
change2(id * 2 + 1, mid + 1, r, pos ,tag);
update(id);
}
int query(int id, int l, int r, int ql, int qr)
{
if(ql == l && qr == r)
{
return seg[id].mav;
}
int mid = (l + r) >> 1;
if(qr <= mid)
return query(id * 2, l, mid, ql, qr);
else if(ql > mid)
return query(id * 2 + 1, mid + 1, r, ql, qr);
else
return max(query(id * 2, l, mid, ql, mid), query(id * 2 + 1, mid + 1, r, mid + 1, qr));
}
int query2(int id, int l, int r, int ql, int qr)
{
if(ql == l && qr == r)
{
return seg[id].val;
}
int mid = (l + r) >> 1;
if(qr <= mid)
return query2(id * 2, l, mid, ql, qr);
else if(ql > mid)
return query2(id * 2 + 1, mid + 1, r, ql, qr);
else
return query2(id * 2, l, mid, ql, mid) + query2(id * 2 + 1, mid + 1, r, mid + 1, qr);
}
void solve()
{
cin>>n;
vx.push_back(0);
for(int i = 1; i <= 12 * n; i++)
seg[i].mav = -1e9;
for(int i = 1; i <= n; i++)
{
cin>>a[i][0]>>a[i][1];
a[i][1] = a[i][0] + a[i][1] - 1;
a[i][2] = i;
vx.push_back(a[i][0]);
vx.push_back(a[i][0] + 1);
vx.push_back(a[i][1]);
}
sort(vx.begin(), vx.end());
vx.erase(unique(vx.begin(), vx.end()), vx.end());
int m = vx.size();
sort(a + 1, a + 1 + n);
for(int i = n; i >= 1; i--)
{
int l = a[i][0] + 1, r = a[i][1];
int pl = lower_bound(vx.begin(), vx.end(), l) - vx.begin() + 1;
int pr = lower_bound(vx.begin(), vx.end(), r) - vx.begin() + 1;
r= max(query(1, 1, m, pl, pr), r);
pr = lower_bound(vx.begin(), vx.end(), r) - vx.begin() + 1;
pl = lower_bound(vx.begin(), vx.end(), l - 1) - vx.begin() + 1;
change(1, 1, m, pl, r);
pl = lower_bound(vx.begin(), vx.end(), l - 1) - vx.begin() + 1;
change2(1, 1, m, pl, 1);
pl = lower_bound(vx.begin(), vx.end(), l) - vx.begin() + 1;
res[a[i][2]] = query2(1, 1, m, pl, pr) + 1;
}
for(int i = 1; i <= n; i++)
cout<<res[i]<<' ';
cout<<'\n';
return;
}
Codeforces Round 212 (Div. 2) C. Insertion Sort
一眼 \(O(N ^ 2)\) ,鉴定为真
不错的好题
冒泡排序交换相邻元素的次数是逆序对之和,也就是说交换两个元素\(i,j(i \ne j,1 \leq i \leq n)\) ,要使得逆序对之和最小,交换两个元素会对逆序对之和产生什么变化呢?
设逆序对 \(f(x) = \sum_{i = 1}^{x - 1} [a_i > a_x]\), 逆序对之和为 \(sum = \sum_{i = 1}^{n}f(i)\),有这么一个序列 \(\dots,a_x,\dots,a_y,\dots\) 考虑交换 \(a\) ,\(b\)
交换后的逆序对为
那么步骤:
-
我们先算出各个点的逆序对\(f(x)\)
-
预处理出交换后逆序对变化(注意x向前交换和向后交换计算方式不一样)
-
暴力枚举 \(x\) ,\(y\)
const int N = 5e3 + 10;
template<class T>
struct BIT {
T c[N];
int size;
void resize(int s) { size = s;}
T query(int x) { // 1 ... x
assert(x <= size);
T s = 0;
for (; x; x -= x & (-x)) {
s += c[x];
}
return s;
}
void modify(int x, T s) { // a[x] += s
assert(x != 0);
for (; x <= size; x += x & (-x)) {
c[x] += s;
}
}
};
BIT<ll> tr;
int n, a[N], b[N][N], ni[N];
void solve()
{
cin>>n;
for(int i = 1; i <= n; i++)
{
cin>>a[i];
a[i]++;
}
tr.resize(n);
int s = 0;
for(int i = 1; i <= n; i++)
{
ni[i] = tr.query(n) - tr.query(a[i]);
s += ni[i];
tr.modify(a[i], 1);
}
for(int i = 1; i <= n; i++)
{
b[i][i] = ni[i];
for(int j = i - 1; j >= 1; j--)
{
b[i][j] = b[i][j + 1];
if(a[j] > a[i]) b[i][j]--;
else if(a[i] > a[j]) b[i][j]++;
}
for(int j = i + 1; j <= n; j++)
{
b[i][j] = b[i][j - 1];
if(a[j] < a[i]) b[i][j]--;
else if(a[j] > a[i]) b[i][j]++;
}
}
int miv = s, res = 0;
//cout<<s<<'\n';
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
{
int t = b[i][j - 1] + b[j][i + 1];
//cout<<i<<" "<<j<<" "<<t<<" "<<s - ni[i] - ni[j] + t + -(a[i] > a[j]) + (a[j] > a[i])<<'\n';
if(s - ni[i] - ni[j] + t + -(a[i] > a[j]) + (a[j] > a[i]) < miv)
{
res= 0;
miv = s - ni[i] - ni[j] + t + -(a[i] > a[j]) + (a[j] > a[i]);
}
if(s - ni[i] - ni[j] + t + -(a[i] > a[j]) == miv)
res++;
}
cout<<miv<<" "<<res.size()<<'\n';
return;
}
VK Cup 2015 - Round 2 (unofficial online mirror, Div. 1 only) B. Work Group
树形dp
满足节点 \(u\) 其子树大小为奇数,偶数均可,区别在于有没有考虑 \(u\) 结点本身
对于转移方程
const int N = 2e5 + 10;
int n;
ll w[N], f[N][2];
// 0 有自己 1 没有自己
vector<int> e[N];
void dfs(int u, int from)
{
f[u][1] = -(1ll << 60);
for(auto v : e[u])
{
if(v == from) continue;
dfs(v, u);
ll a = f[u][0], b = f[u][1];
f[u][0] = max(a + f[v][0], b + f[v][1]);
f[u][1] = max(a + f[v][1], b + f[v][0]);
}
f[u][1] = max(f[u][0] + w[u], f[u][1]);
}
void solve()
{
cin>>n;
for(int i = 1; i <= n; i++)
{
int p; cin>>p>>w[i];
if(p != -1) e[p].push_back(i);
}
dfs(1, 0);
// for(int i = 1; i <= n; i++)
// cout<<f[i]<<'\n';
cout<<max(f[1][0], f[1][1])<<'\n';
return;
}
Codeforces Round 874 (Div. 3) F. Ira and Flamenco
思维题
问长度为 \(k\) ,公差为 \(1\),的等差数列有多少个,离散化,双指针判断即可
ll qmi(ll a, ll b, ll mod)
{
ll ans = 1 % mod;
while(b)
{
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int n, k;
map<int, int> mp;
/*
8 2
1 1 2 2 3 3 3 5
1 2 3 5
*/
void solve()
{
vector<int> a;
mp.clear();
cin>>n>>k;
for(int i = 1; i <= n; i++)
{
int x; cin>>x;
mp[x]++; a.push_back(x);
}
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end());
int res = 0, f = 1;
int m = a.size();
for(int i = 0; i < m; i++)
{
if(i + k - 1 < m && a[i] + k - 1 == a[i + k - 1])
{
f = 1;
for(int j = i; j <= i + k - 1; j++)
f = 1ll * f * mp[a[j]] % mod;
res = (1ll * res + f) % mod;
int l = i, r = i + k - 1;
while(r + 1 < m && a[r + 1] == a[r] + 1)
{
f = 1ll * f * qmi(mp[a[l]], mod - 2, mod) % mod;
f = 1ll * f * mp[a[r + 1]] % mod;
res = (1ll * res + f) % mod;
l++, r++;
}
i = r;
}
}
cout<<res<<'\n';
return;
}
Codeforces Round 455 (Div. 2) D - Colorful Points
链表
同色分为一组,通过双链表去进行删除和合并操作,复杂度 \(O(N)\)
const int N = 1e6 + 10;
int m, l[N], r[N], e[N], num[N], times[N], idx;
void insert(int a, int x, int w, int t)
{
times[idx] = t;
num[idx] = w;
e[idx] = x;
l[idx] = a;
l[r[a]]= idx;
r[idx] = r[a];
r[a] = idx;
idx++;
}
void del(int a)
{
r[l[a]] = r[a];
l[r[a]] = l[a];
}
void solve()
{
string t; cin>>t;
int n = t.size(), k = 1;
r[0] = 1, l[1] = 0, idx = 2;
for(int i = 0; i < n; i++)
{
int x = i, y = i;
while(y + 1 < n && t[y + 1] == t[x])
y++;
insert(l[1], y - x + 1, t[i] - 'a' + 1, idx);
k++;
i = y;
}
int res = 0, tt = 0;
while(1)
{
bool ok = false;
vector<int> d;
for(int i = r[0]; i != 1; i = r[i])
{
int tl = l[i], tr = r[i];
if(tl == tr) break;
if(tl != 0 && num[tl] != num[i])
{
ok = true;
e[i]--;
}
if(tr != 1 && num[tr] != num[i])
{
ok = true;
e[i]--;
}
if(tr != 1 && num[tr] == num[i])
{
e[tr] = e[tr] + max(e[i], 0);
e[i] = 0;
}
if(e[i] <= 0)
d.push_back(times[i]);
}
if(!ok) break;
for(auto it : d)
del(it);
res++;
}
cout<<res<<'\n';
return;
}
Codeforces Round 870 (Div. 2) D. Running Miles
问 \(b_i + b_j + b_k - (r - l), i \ne j, i \ne k, j \ne k\) 的最大值
变化一下\(b_l + l + b_j + b_r - r\)
为什么呢对于一段区间 \([l, r]\), 显然使\(l\) 越大, \(r\) 越小是最优的
预处理出后缀 \(b_r - r\) 的最大值,再预处理每个点加上后缀最大值的值 \(b_j + \max_{r = j + 1}^{n} (b_r - r)\) ,再预处理 \(b_j + \max_{r = j + 1}^{n} (b_r - r)\) 的后缀最大值
总的来说就是后缀最大值的嵌套
const int N = 2e5 + 10;
int n, a[N], pre[N], mid[N], suf[N];
void solve()
{
cin>>n;
for(int i = 1; i <= n; i++)
{
cin>>a[i];
pre[i] = a[i] + i;
}
suf[n] = a[n] - n;
for(int i = n - 1; i >= 1; i--)
suf[i] = max(a[i] - i, suf[i + 1]);
mid[n] = -(1 << 29);
for(int i = n - 1; i >= 2; i--)
mid[i] = max(suf[i + 1] + a[i], mid[i + 1]);
int res = 0;
for(int i = 1; i <= n - 2; i++)
res = max(pre[i] + mid[i + 1], res);
cout<<res<<"\n";
return;
}
Codeforces Round 179 (Div. 2) B. Yaroslav and Two Strings
一眼dp
$ f_{i,0,1,2,3}$ 表示情况都不满足,情况\(s < w\) 满足,, 情况 \(s > w\) 满足,情况 \(s < w, s > w\) 满足,相互之间转移即可
int n;
int f[N][4];
string s, t;
// 0 1 2 3
// 0 < > <>
void solve()
{
cin>>n;
cin>>s>>t;
s = "?" + s, t = "?" + t;
int S = 0;
for(int i = 1; i <= n; i++)
if(s[i] == '?' || t[i] == '?')
continue;
else if(s[i] < t[i])
S = S | (1 << 1);
else if(s[i] > t[i])
S = S | (1 << 2);
if(S == 0) f[0][0] = 1;
else if(S == 2) f[0][1] = 1;
else if(S == 4) f[0][2] = 1;
else if(S == 6) f[0][3] = 1;
for(int i = 1; i <= n; i++)
{
if(t[i] != '?' && s[i] != '?')
{
f[i][0] = f[i - 1][0];
f[i][1] = f[i - 1][1];
f[i][2] = f[i - 1][2];
f[i][3] = f[i - 1][3];
continue;
}
else if(s[i] == '?' && t[i] == '?')
{
f[i][0] = 1ll * f[i - 1][0] * 10 % mod;
f[i][1] = (1ll * f[i - 1][1] * 55 + 1ll * f[i - 1][0] * 45) % mod;
f[i][2] = (1ll * f[i - 1][2] * 55 + 1ll * f[i - 1][0] * 45) % mod;
f[i][3] = (1ll * f[i - 1][3] * 100 + 1ll * f[i - 1][1] * 45 + 1ll * f[i - 1][2] * 45) % mod;
}
else if(s[i] == '?' || t[i] == '?')
{
// 0 1 2 3
// 0 < > <>
if(s[i] == '?')
{
f[i][0] = f[i - 1][0];
f[i][1] = (1ll * f[i - 1][0] * (t[i] - '0') + 1ll * f[i - 1][1] * (t[i] - '0' + 1)) % mod;
f[i][2] = (1ll * f[i - 1][0] * ('9' - t[i]) + 1ll * f[i - 1][2] * ('9' - t[i] + 1)) % mod;
f[i][3] = (1ll * f[i - 1][3] * 10 + 1ll * f[i - 1][1] * ('9' - t[i]) + 1ll * f[i - 1][2] * (t[i] - '0')) % mod;
}
else
{
f[i][0] = f[i - 1][0];
f[i][1] = (1ll * f[i - 1][0] * ('9' - s[i]) + 1ll * f[i - 1][1] * ('9' - s[i] + 1)) % mod;
f[i][2] = (1ll * f[i - 1][0] * (s[i] - '0') + 1ll * f[i - 1][2] * (s[i] - '0' + 1)) % mod;
f[i][3] = (1ll * f[i - 1][3] * 10 + 1ll * f[i - 1][1] * (s[i] - '0') + 1ll * f[i - 1][2] * ('9' - s[i])) % mod;
}
}
//cout<<i<<" "<<f[i][0]<<" "<<f[i][1]<<" "<<f[i][2]<<" "<<f[i][3]<<"\n-------------------\n";
}
cout<<f[n][3]<<'\n';
return;
}
本文来自博客园,作者:magicat,转载请注明原文链接:https://www.cnblogs.com/magicat/p/17616549.html