D - Directing Edges
题目大意:给定一张图,一些有向边,一些无向边,问是否存在一种方案,对所有的无向边确定方向后不存在环,输出方案
对于不存在方案的情况,即一条边无论选择任意方向都不满足条件,即对于任意方向都成环,即原图有环
所以只建立有向边,进行拓扑,有环就无解
考虑如何输出方案
拓扑排序的时候保存出队的时候的顺序,对于一条有向边,总是先出指向后出,因此对于其他的边,也进行相同的操作,先出指向后出
#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define db double
using namespace std;
int read() {
int rt = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <='9') {
rt = (rt << 3) + (rt << 1) + ch - '0';
ch = getchar();
}
return rt * f;
}
const int maxn = 2e5 + 5;
int n, m, T;
struct Edge {
int nxt, to;
}e[maxn << 1];
int h[maxn], tot, rk[maxn], dgr[maxn], cnt, ton[maxn];
struct e {
int u, v, t;
}E[maxn];
void init() {
for (int i = 1; i <= n; i++) h[i] = 0, dgr[i] = 0;
tot = 0;
cnt = 0;
}
void add(int u, int v) {
tot++;
e[tot].to = v;
e[tot].nxt = h[u];
h[u] = tot;
}
queue<int> q;
void topsort() {
while (!q.empty()) {
int u = q.front();
q.pop();
ton[++cnt] = u;
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
dgr[v]--;
if (!dgr[v]) q.push(v);
}
}
}
int main() {
T = read();
while (T--) {
n = read(), m = read();
init();
for (int i = 1; i <= m; i++) {
E[i].t = read();
E[i].u = read();
E[i].v = read();
if (E[i].t == 1) {
add(E[i].u, E[i].v);
dgr[E[i].v]++;
}
}
cnt = 0;
for (int i = 1; i <= n; i++) {
if (!dgr[i]) q.push(i);
}
topsort();
if (cnt < n) {
puts("NO");
continue;
}
puts("YES");
for (int i = 1; i <= cnt; i++) {
rk[ton[i]] = i;
}
for (int i = 1; i <= m; i++) {
if (rk[E[i].u] < rk[E[i].v]) {
printf("%d %d\n", E[i].u, E[i].v);
} else {
printf("%d %d\n", E[i].v, E[i].u);
}
}
}
return 0;
}
/*
4
3 1
0 1 3
5 5
0 2 1
1 1 5
1 5 4
0 5 2
1 3 5
4 5
1 1 2
0 4 3
1 3 1
0 2 3
1 2 4
4 5
1 4 1
1 1 3
0 1 2
1 2 4
1 3 2
*/
E - Weighting a Tree
题目大意:
给定一张图,给出每个点的一个权值,要求找出一种构造方案,对于每个点,所有边权之和等于这个点的权值
对于一个点及其所有边的情况,考虑生成树之后再调整
对于一棵树,叶子节点只有一条边,边权一定,向上一直更新到根
对于根,如果此时和为给定值,即成立,不为给定值时,就要找到一条非树边,去调整权值
一条非树边有两种情况,成奇环或成偶环
偶环改变权值对根无影响,奇环造成两倍绝对值的影响,所以只需要找到一个奇环,改2倍权值并改变LCA中间的所有边权
代码待填
F - Sasha and Array
题目大意:
给定一个初始序列,有两个操作
1.区间加x
2.求区间f(x)的和 f(x)为斐波那契数列第x项
考试忘了矩阵分配律
斐波那契数列有矩阵加速的求法
1 1
1 0
左乘该矩阵即得到下一项
区间加x就是乘这个矩阵的x次幂
线段树维护矩阵就好了
结构体内部初值不为0!
#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define db double
using namespace std;
ll read() {
ll rt = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <='9') {
rt = (rt << 3) + (rt << 1) + ch - '0';
ch = getchar();
}
return rt * f;
}
const int maxn = 1e5 + 5;
const int Mod = 1e9 + 7;
int n, m;
ll a[maxn];
struct Mat {
ll a[3][3];
}f[maxn << 2], tag[maxn << 2], s;
bool flag[maxn << 2];
Mat newnode() {
Mat rt;
for (int i = 1; i <= 2; i++) {
for (int j = 1; j <= 2; j++) rt.a[i][j] = 0;
}
return rt;
}
Mat add(Mat x, Mat y) {
Mat rt;
for (int i = 1; i <= 2; i++) {
for (int j = 1; j <= 2; j++) {
rt.a[i][j] = x.a[i][j] + y.a[i][j];
if (rt.a[i][j] >= Mod) rt.a[i][j] -= Mod;
}
}
return rt;
}
Mat mul(Mat x, Mat y) {
Mat rt = newnode();
for (int i = 1; i <= 2; i++) {
for (int j = 1; j <= 2; j++) {
for (int k = 1; k <= 2; k++) {
rt.a[i][j] += x.a[i][k] * y.a[k][j] % Mod;
rt.a[i][j] %= Mod;
}
}
}
return rt;
}
Mat mpow(Mat x, ll b) {
Mat rt = newnode();
if (b == -1) {
rt.a[1][2] = 1;
rt.a[2][1] = 1;
rt.a[2][2] = -1;
return rt;
}
for (int i = 1; i <= 2; i++) rt.a[i][i] = 1;
while (b) {
if (b & 1) rt = mul(rt, x);
b >>= 1;
x = mul(x, x);
}
return rt;
}
void update(int o) {
f[o] = add(f[o << 1], f[o << 1 | 1]);
}
void build(int o, int lf, int rg) {
tag[o] = newnode();
tag[o].a[1][1] = tag[o].a[2][2] = 1;
flag[o] = 0;
if (lf == rg) {
f[o] = mpow(s, a[lf] - 2);
return ;
}
int mid = (lf + rg) >> 1;
build(o << 1, lf, mid);
build(o << 1 | 1, mid + 1, rg);
update(o);
}
void pushdown(int o) {
if (flag[o]) {
flag[o << 1] = flag[o << 1 | 1] = 1;
tag[o << 1] = mul(tag[o << 1], tag[o]);
tag[o << 1 | 1] = mul(tag[o << 1 | 1], tag[o]);
f[o << 1] = mul(f[o << 1], tag[o]);
f[o << 1 | 1] = mul(f[o << 1 | 1], tag[o]);
tag[o] = newnode();
tag[o].a[1][1] = tag[o].a[2][2] = 1;
flag[o] = 0;
}
}
void modify(int o, int lf, int rg, int L, int R, Mat d) {
if (L <= lf && rg <= R) {
f[o] = mul(f[o], d);
tag[o] = mul(tag[o], d);
flag[o] = 1;
return ;
}
pushdown(o);
int mid = (lf + rg) >> 1;
if (L <= mid) modify(o << 1, lf, mid, L, R, d);
if (R > mid) modify(o << 1 | 1, mid + 1, rg, L, R, d);
update(o);
}
Mat query(int o, int lf, int rg, int L, int R) {
if (L <= lf && rg <= R) {
return f[o];
}
pushdown(o);
Mat rt = newnode();
int mid = (lf + rg) >> 1;
if (L <= mid) rt = add(rt, query(o << 1, lf, mid, L, R));
if (R > mid) rt = add(rt, query(o << 1 | 1, mid + 1, rg, L, R));
return rt;
}
int main() {
n = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read();
s = newnode();
s.a[1][1] = s.a[1][2] = s.a[2][1] = 1;
build(1, 1, n);
while (m--) {
int opt = read();
if (opt == 1) {
int L = read(), R = read();
ll x = read();
Mat tmp = mpow(s, x);
modify(1, 1, n, L, R, tmp);
// Mat now = query(1, 1, n, 13, 13);
// printf("%lld\n", (now.a[1][1] + now.a[1][2]) % Mod);
// Mat noww = query(1, 1, n, 14, 14);
// printf("%lld\n", (noww.a[1][1] + noww.a[1][2]) % Mod);
//printf("%lld\n", (now.a[1][1] + now.a[1][2] + noww.a[1][1] + noww.a[1][2]) % Mod);
} else {
int L = read(), R = read();
Mat x = query(1, 1, n, L, R);
printf("%lld\n", (x.a[1][1] + x.a[1][2]) % Mod);
}
}
return 0;
}
/*
18 31
1 1 2 2 2 1 2 1 2 2 2 2 1 1 1 2 2 1
1 4 7 2
1 9 10 1
1 2 4 2
1 15 18 2
1 3 10 2
1 7 8 1
1 13 18 2
1 15 17 2
1 16 17 1
1 16 16 2
1 10 12 1
1 4 13 2
1 3 8 1
1 7 9 1
1 13 18 2
1 3 14 2
1 2 2 1
1 14 17 1
1 9 18 1
1 7 17 1
1 2 5 1
1 8 13 2
1 12 18 1
2 13 14
2 15 18
1 14 14 2
1 8 14 2
1 3 11 2
1 17 17 2
1 7 10 2
2 3 12
*/
G - Cats Transport
题目大意:
小 S 是农场主,他养了 M MM 只猫,雇了 P PP 位饲养员。农场中有一条笔直的路,路边有 N NN 座山,从 1 11 到 N NN 编号。第 i ii 座山与第 i − 1 i-1i−1 座山之间的距离是 D i D_iDi 。饲养员都住在 1 11 号山上。
有一天,猫出去玩。第 iii 只猫去 H i H_iHi号山玩,玩到时刻 T i T_iTi停止,然后在原地等饲养员来接。饲养员们必须回收所有的猫。每个饲养员沿着路从 1 11 号山走到 N NN 号山,把各座山上已经在等待的猫全部接走。饲养员在路上行走需要时间,速度为 1 11 米每单位时间。饲养员在每座山上接猫的时间可以忽略,可以携带的猫的数量为无穷大。
例如有两座相距为 1 11 的山,一只猫在 2 22 号山玩,玩到时刻 3 33 开始等待。如果饲养员从 1 11 号山在时刻 2 22 或 3 33 出发,那么他可以接到猫,猫的等待时间为 0 00 或 1 11。而如果他于时刻 1 11 出发,那么他将于时刻 2 22 经过 2 22 号山,不能接到当时仍在玩的猫。
你的任务是规划每个饲养员从 1 11 号山出发的时间,使得所有猫等待时间的总和尽量小。饲养员出发的时间可以为负。
T时间到达d的饲养员和T - d从1出发的饲养员等价
可以把所有猫看成都在1号节点,对于出现的时间为T - d
sort一下之后就是将这么多只猫分成p份的最小权值
dp过程中可以进行斜率优化,复杂度m * p,可以滚动减少空间
#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define db double
using namespace std;
ll read() {
ll rt = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <='9') {
rt = (rt << 3) + (rt << 1) + ch - '0';
ch = getchar();
}
return rt * f;
}
const int maxn = 1e5 + 5;
ll dp[maxn][105], d[maxn], c[maxn], sum[maxn];
ll q[maxn], n, m, p;
db slope(int i, int j, int k) {
return (1.0 * (sum[i] + dp[i][k - 1] - sum[j] - dp[j][k - 1])) / (1.0 * (i - j));
}
int main() {
n = read(), m = read(), p = read();
for (int i = 2; i <= n; i++) {
d[i] = read();
d[i] += d[i - 1];
}
for (int i = 1; i <= m; i++) {
ll h = read(), t = read();
c[i] = t - d[h];
}
sort(c + 1, c + 1 + m);
for (int i = 1; i <= m; i++) sum[i] = sum[i - 1] + c[i];
memset(dp, 0x3f, sizeof(dp));
dp[0][0] = 0;
for (int j = 1; j <= p; j++) {
int head = 1, tail = 0;
q[++tail] = 0;
for (int i = 1; i <= m; i++) {
while (head < tail && slope(q[head], q[head + 1], j) < 1.0 * c[i]) head++;
dp[i][j] = min(dp[i][j], dp[q[head]][j - 1] + (i - q[head]) * c[i] - sum[i] + sum[q[head]]);
while (head < tail && slope(q[tail - 1], q[tail], j) > slope(q[tail], i, j)) tail--;
q[++tail] = i;
}
}
cout << dp[m][p] << endl;
return 0;
}
H的NTT乱搞不会,爪巴