昨晚差点被D卡死,然后E题题意一直没读对,导致线段树水题给漏了
D. Coloring Edges
题意:给你一个有向图,你要给每一条边涂色,问最少需要多少种颜色,使得如果存在环,那么环上的颜色不能全部相同。
解法:首先我们用拓扑排序判环,无环一种颜色够了,如果有环,其实最多两种颜色足够,因为一个有向边构成的环,肯定存在有向边 u -> v,u 大于 v,也肯定存在有向边 u -> v,u 小于 v,因此我们根据 u v 的大小关系去给每条边染色就好了。
#include<bits/stdc++.h>
#define pi pair<int, int>
#define mk make_pair
using namespace std;
const int maxn = 5000 + 10;
int d[maxn], n, ok = 1, vis[maxn];
vector<pi> G[maxn];
int check() {
queue<int> q;
for (int i = 1; i <= n; i++)
if (!d[i])
q.push(i);
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto v : G[u]) {
vis[v.second] = 1;
if (--d[v.first] == 0)
q.push(v.first);
}
}
for (int i = 1; i <= n; i++)
if (d[i])
return 0;
return 1;
}
int main() {
int m, u, v;
cin>>n>>m;
for (int i = 1; i <= m; i++) {
cin>>u>>v;
d[v]++;
G[u].push_back(mk(v, i));
}
if (check()) {
puts("1");
for (int i = 1; i <= m; i++)
printf("1 ");
return 0;
}
puts("2");
for (int i = 1; i <= n; i++)
for (auto tmp : G[i]) {
int id = tmp.second;
int to = tmp.first;
if (!vis[id]) {
if (i < to)
vis[id] = 1;
else
vis[id] = 2;
}
}
for (int i = 1; i <= m; i++)
printf("%d ", vis[i]);
}
题意:定义合法集合:设集合所有数的和为sum,那么sum十进制的每一位数,集合都存在一个数十进制下对应的数与其相等,求区间内非法集合的最小sum。
解法:我们发现,其实最少只要两个数就能构造非法集合,只要十进制下这两个数存在某位都不为0,比如310,20,十位上两个数都不为0,那么310,20构成的集合肯定非法,因此,我们用10个线段树分别维护十进制下每位数不为0的最小值次小值即可,比如1234,200,5,233,那么第1颗线段树维护的是个位上不为0的最小值次小值分别为5,233,第2颗线段树维护的是十位上不为0的最小值次小值分别为233,1234,第3颗线段树维护的是百位的200,233,后面亦同理。我们用线段树查每一位的最小值次小值,然后相加取min就是答案。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5 + 10, inf = 2e9 + 1;
struct node {
int mn[10], mn2[10];
node operator+(const node& t) const {
node tmp;
for (int i = 0; i < 10; i++) {
tmp.mn[i] = min(mn[i], t.mn[i]);
if (mn[i] < t.mn[i])
tmp.mn2[i] = min(t.mn[i], mn2[i]);
else
tmp.mn2[i] = min(mn[i], t.mn2[i]);
}
return tmp;
}
} tree[maxn * 4];
#define ls o * 2
#define rs o * 2 + 1
#define m (l + r) / 2
void up(int o, int l, int r, int k, int v) {
if (l == r) {
for (int i = 0; i < 10; i++)
tree[o].mn[i] = tree[o].mn2[i] = inf;
for (int i = 0, x = v; i < 10; i++, x /= 10)
if (x % 10)
tree[o].mn[i] = v;
return;
}
if (k <= m)
up(ls, l, m, k, v);
else
up(rs, m + 1, r, k, v);
tree[o] = tree[ls] + tree[rs];
}
node qu(int o, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr)
return tree[o];
if (qr <= m)
return qu(ls, l, m, ql, qr);
else if (ql > m)
return qu(rs, m + 1, r, ql, qr);
else
return qu(ls, l, m, ql, qr) + qu(rs, m + 1, r, ql, qr);
}
int main() {
int n, q, x, opt, l, r;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
up(1, 1, n, i, x);
}
while (q--) {
scanf("%d%d%d", &opt, &l, &r);
if (opt == 1)
up(1, 1, n, l, r);
else {
ll ans = inf;
node tmp = qu(1, 1, n, l, r);
for (int i = 0; i < 10; i++)
ans = min(ans, 1ll * tmp.mn[i] + tmp.mn2[i]);
if (ans >= inf)
ans = -1;
printf("%lld\n", ans);
}
}
}