1001.Ticket
签到
1002. Gcd
签到
1003. Function
解法:我们用一个优先队列存fi(x + 1) - fi(x),初始化我们把所有方程的x设置为1,并且全部存进优先队列,最小值优先,每次出队,我们都把自变量调大一个单位继续进队,依次进出队m - n 次,最后取出优先队列所有函数值并求和即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
ll d[maxn];
int vis[maxn], n, m;
struct node {
int a, b, c, x;
ll v;
bool operator<(const node& t) const {
return v > t.v;
}
};
priority_queue<node> q;
ll gao(int a, int b ,int c, int x) {
return a * x * x + b * x + c;
}
int main() {
int a, b, c;
cin>>n>>m;
for (int i = 1; i <= n; i++) {
cin>>a>>b>>c;
ll v = gao(a, b, c, 2) - gao(a, b, c, 1);
q.push(node{a, b, c, 1, v});
}
for (int i = 0; i < m - n; i++) {
node tmp = q.top();
q.pop();
tmp.x++;
tmp.v = gao(tmp.a, tmp.b, tmp.c, tmp.x + 1) - gao(tmp.a, tmp.b, tmp.c, tmp.x);
q.push(tmp);
}
ll ans = 0;
while (!q.empty()) {
node tmp = q.top();
ans += gao(tmp.a, tmp.b, tmp.c, tmp.x);
q.pop();
}
cout<<ans<<endl;
}
1004. Tree
裸树链剖分
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
ll sum[maxn *4];
int mx[maxn * 4], top[maxn], dep[maxn], son[maxn];
int sz[maxn], cnt, id[maxn], f[maxn], n, a[maxn];
vector<int> G[maxn];
void dfs(int u, int fa) {
f[u] = fa;
dep[u] = dep[fa] + 1;
sz[u] = 1;
for (auto v : G[u])
if (v != fa) {
dfs(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]])
son[u] = v;
}
}
#define ls o * 2
#define rs o * 2 + 1
void update(int o, int l, int r, int k, int v) {
if (l == r) {
mx[o] = sum[o] = v;
return;
}
int m = (l + r) / 2;
if (k <= m)
update(ls, l, m, k, v);
else
update(rs, m + 1, r, k, v);
mx[o] = max(mx[ls], mx[rs]);
sum[o] = sum[ls] + sum[rs];
}
void dfs2(int u, int rt) {
top[u] = rt;
id[u] = ++cnt;
update(1, 1, n, cnt, a[u]);
if (son[u])
dfs2(son[u], rt);
for (auto v : G[u])
if (v != f[u] && v != son[u])
dfs2(v, v);
}
void up(int o, int l, int r, int ql, int qr) {
if (mx[o] <= 1)
return;
int m = (l + r) / 2;
if (l >= ql && r <= qr) {
if (l == r) {
sum[o] = mx[o] = sqrt(mx[o]);
return;
}
if (mx[ls] > 1)
up(ls, l, m, ql, qr);
if (mx[rs] > 1)
up(rs, m + 1, r, ql, qr);
mx[o] = max(mx[ls], mx[rs]);
sum[o] = sum[ls] + sum[rs];
return;
}
if (ql <= m)
up(ls, l, m, ql, qr);
if (qr > m)
up(rs, m + 1, r, ql, qr);
mx[o] = max(mx[ls], mx[rs]);
sum[o] = sum[ls] + sum[rs];
}
ll qu(int o, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr)
return sum[o];
int m = (l + r) / 2;
ll res = 0;
if (ql <= m)
res += qu(ls, l, m, ql, qr);
if (qr > m)
res += qu(rs, m + 1, r, ql, qr);
return res;
}
void up_path(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]])
swap(x, y);
up(1, 1, n, id[top[x]], id[x]);
x = f[top[x]];
}
if (id[x] > id[y])
swap(x, y);
up(1, 1, n, id[x], id[y]);
}
ll qu_path(int x, int y) {
ll res = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]])
swap(x, y);
res += qu(1, 1, n, id[top[x]], id[x]);
x = f[top[x]];
}
if (id[x] > id[y])
swap(x, y);
return res + qu(1, 1, n, id[x], id[y]);
}
int main() {
int m, opt, u, v;
cin>>n>>m;
for (int i = 1; i <= n; i++)
cin>>a[i];
for (int i = 1; i < n; i++) {
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
dfs2(1, 1);
while (m--) {
cin>>opt>>u>>v;
if (!opt)
up_path(u, v);
else
printf("%lld\n", qu_path(u, v));
}
}
解法:我们用map[u]存u节点所有儿子权值信息,然后进行一次dfs,枚举每个节点u当领导的贡献,首先 u 的原领导 rt 离职了,那么ans -= mp[rt][a[rt]],然后我们要把 u 的儿子和 rt 的儿子合并,枚举 u 所有儿子权值 val,ans += mp[u][val] * mp[rt][val],不过 u 作为 rt 的儿子和 u 的儿子合并多计算了一次贡献,所以ans -= mp[u][a[u]]
update:上述做法没考虑周全,已被评论区数据hack(感谢),如果 rt 还有父亲 root,那么我们还用统计 rt 变成 u 之后对 root 以及 root 的儿子产生的贡献的变化,这个问题很简单,交给你啦
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
map<int, int> mp[maxn];
int a[maxn], f[maxn];
vector<int> G[maxn];
ll sum, ans[maxn];
void dfs(int u, int fa) {
f[u] = fa;
for (auto v : G[u])
if (v != fa) {
dfs(v, u);
if (a[v] == a[u])
sum++;
sum += mp[u][a[v]];
mp[u][a[v]]++;
}
}
void dfs2(int u) {
int rt = f[u];
if (rt) {
ll res = sum - mp[rt][a[rt]];
for (auto tmp : mp[u])
res += 1ll * tmp.second * mp[rt][tmp.first];
res -= mp[u][a[u]];
int root = f[rt];
if (root) {
res -= (mp[root][a[rt]] - 1);
if (a[root] == a[rt])
res--;
if (a[u] == a[root])
res++;
res += mp[root][a[u]];
}
ans[rt] = max(ans[rt], res);
}
if (G[u].size() == 1 && u != 1) {
ans[u] = sum - mp[rt][a[u]] + 1;
if (a[rt] == a[u])
ans[u]--;
}
for (auto v : G[u])
if (v != f[u])
dfs2(v);
}
int main() {
int n, m, u, v;
cin>>n>>m;
for (int i = 1; i <= n; i++)
cin>>a[i];
for (int i = 1; i < n; i++) {
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
dfs2(1);
for (int i = 1; i <= n; i++)
printf("%lld%c", ans[i], i == n ? '\n' : ' ');
}
解法:我们设d[i][j][p]为前 i 个字符,含有 p 段字符串,最后一个字母是 j + ‘a’ 所需要最少的操作数,对于第 i 个字符 c,我可以不修改它,d[i][c][p] = min(d[i][c][p], d[i - 1][c][p]),对于所有 j (j != c),d[i][c][p] = min(d[i][c][p], d[i - 1][j][p - 1]),我们修改它,那么最好是直接整段修改到 pre= max(0, i - L),枚举字符 c,对于所有 j != c,显然d[i][c][p] = min(d[i][c][p], d[pre][j][p - 1] + 1),我们可以预处理出d[pre][j][p - 1] 的前缀最大值,后缀最大值,再进行转移,复杂度 n * 26 * K。//最后答案应该还有枚举段数,之前写瑕疵了,感谢网友的指出
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int d[maxn][26][11], sum[28], suf[28];
char s[maxn];
void up(int &x, int y) {
x = min(x, y);
}
int main() {
int n, l, k;
cin>>n>>l>>k>>s + 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j < 26; j++)
for (int p = 0; p <= k; p++)
d[i][j][p] = 1e9;
d[1][s[1] - 'a'][1] = 0;
for (int i = 0; i < 26; i++)
up(d[1][i][1], 1);
for (int i = 2; i <= n; i++)
for (int p = 1; p <= min(i, k); p++) {
up(d[i][s[i] - 'a'][p], d[i - 1][s[i] - 'a'][p]);
for (int j = 0; j < 26; j++)
if (j != s[i] - 'a')
up(d[i][s[i] - 'a'][p], d[i - 1][j][p - 1]);
int pre = max(0, i - l);
suf[25] = d[pre][25][p - 1];
for (int j = 24; j >= 0; j--)
suf[j] = min(suf[j + 1], d[pre][j][p - 1]);
int mn = d[pre][0][p - 1];
for (int j = 0; j < 26; j++) {
up(d[i][j][p], d[pre][j][p] + 1);
int t1 = 1e9, t2 = 1e9;
if (j)
up(t1, mn);
if (j != 25)
up(t2, suf[j + 1]);
mn = min(mn, d[pre][j][p - 1]);
up(d[i][j][p], min(t1, t2) + 1);
}
}
int ans = 1e9;
for (int i = 0; i < 26; i++)
for (int j = 1; j <= k; j++)
up(ans, d[n][i][j]);
cout<<ans<<endl;
}
/*
4 1 4
bbbb
*/
1007. Circle
咕咕咕
1008. Clock
咕咕咕
1009. Union
没读题(应该不会)
解法:我们发现,加一条线,最多有5条线相交,那么 ans += 6,再加一条线,就会有6条线与之相交,那么ans += 7,那么很显然,加n条边的总区域数是:7 + (6 + n + 5) * n / 2。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
ll n;
while (cin>>n) {
ll res = 7 + (11 + n) * n / 2;
cout<<res << '\n';
}
}
1011. Tetris
咕咕咕