A 计划日
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
bool leapyear(int y)
{
if (y % 400 == 0 || y % 4 == 0 && y % 100 != 0)
return true;
return false;
}
int main()
{
#ifdef LOCAL
//freopen("C:/input.txt", "r", stdin);
#endif
int month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int T;
cin >> T;
while (T--)
{
int y, m, d, w, n;
scanf("%4d%2d%2d %d %d", &y, &m, &d, &w, &n);
//cout << y << " " << m << " " << d << endl;
--w;
while (n--)
{
d++;
if (d > month[m] + (m == 2 && leapyear(y)))
d = 1, m++;
if (m > 12)
y++, m = 1;
w = (w + 1) % 7;
}
printf("%04d%02d%02d %d\n", y, m, d, w + 1);
}
return 0;
}
B 治安管理 <前缀和>
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
int sum[N], a[N], b[N];
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int T;
cin >> T;
while (T--)
{
memset(sum, 0, sizeof(sum));
int n, m, s, f;
cin >> n >> m >> s >> f;
for (int i = 0; i < n; ++i)
scanf("%d", &a[i]);
for (int i = 0; i < n; ++i)
scanf("%d", &b[i]);
for (int i = 0; i < n; ++i)
sum[a[i]]++, sum[b[i]]--;
int mx = 0, mi = INF;
for (int i = 0; i < N; ++i)
{
sum[i] += sum[i - 1];
if (i >= s && i < f)
mx = max(mx, sum[i]), mi = min(mi, sum[i]);
}
if (mi >= m)
printf("YES %d\n", mx);
else
printf("NO %d\n", mi);
}
return 0;
}
C 山区修路 <dp>
状态转移时使用u、d两个数组求出上个状态高度从第到高、从高到低的最小值前缀。然后进行转移减少复杂度。
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
int f[N][110]; //第i个位置为j高度的代价
int u[110], d[110];
int a[N];
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int T;
cin >> T;
while (T--)
{
memset(f, 0x3f, sizeof(f));
memset(u, 0, sizeof(u));
memset(d, 0, sizeof(d));
int n, x;
cin >> n >> x;
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
u[0] = d[101] = INF;
for (int i = 1; i <= n; ++i) //位置
{
for (int j = a[i]; j <= 100; ++j) //当前高度
{
f[i][j] = (a[i] - j) * (a[i] - j);
f[i][j] += min(u[j], d[j]); //前面状态转移到j高度的代价
}
memset(u, 0x3f, sizeof(u)); //最值前缀
memset(d, 0x3f, sizeof(d));
for (int j = 1; j <= 100; ++j)
u[j] = min(u[j - 1] + x, f[i][j]);
for (int j = 100; j >= 1; --j)
d[j] = min(d[j + 1] + x, f[i][j]);
}
int ans = INF;
for (int i = 1; i <= 100; ++i)
ans = min(ans, f[n][i]);
cout << ans << endl;
}
return 0;
}
D 求XF+闭包 <模拟>
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
string a[50], b[50], ans;
int n, m, k;
bool solve()
{
for (int i = 0; i < k; ++i)
{
//cout << a[i] << " " << b[i] << endl;
int flag = 1;
for (int j = 0; j < a[i].size(); ++j)
if (ans.find(a[i][j]) == -1)
flag = 0;
if (flag)
{
int sz = ans.size();
ans += b[i];
sort(ans.begin(), ans.end());
ans.erase(unique(ans.begin(), ans.end()), ans.end());
if (sz < ans.size())
return 1;
}
}
return 0;
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int T;
cin >> T;
while (T--)
{
cin >> n >> m >> k;
cin >> ans >> ans;
for (int i = 0; i < k; ++i)
cin >> a[i] >> b[i];
while (solve());
cout << ans << endl;
}
return 0;
}
E 物流配送 <费用流>
把每个点的需求量和原有量进行比较,原有较多的作为源点,需求较多的作为汇点。建立超级源点和所有源点相连,容量为原有量-需求量,代价为0,建立超级汇点类似操作。
题目所给边建立双向边,代价为题目所给容量无穷大。最后跑最小费用最大流,最小代价即为答案。
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e3 + 10;
int a[N], b[N];
bool vis[N];
int dis[N], pre[N];
struct edge
{
int v, w, c, nxt; //容量 代价
}e[N * 8];
int h[N], idx;
void AddEdge(int u, int v, int w, int c)
{
e[idx] = { v, w, c, h[u] };
h[u] = idx++;
}
bool SPFA(int st, int ed)
{
queue<int> q;
memset(dis, 0x3f, sizeof(dis));
memset(pre, -1, sizeof(pre));
dis[st] = 0;
vis[st] = 1;
q.push(st);
while (!q.empty())
{
int u = q.front(); q.pop(), vis[u] = 0;
for (int i = h[u]; ~i; i = e[i].nxt)
{
int v = e[i].v, w = e[i].w, c = e[i].c; //w流量 c代价
if (dis[v] > dis[u] + c && w)
{
dis[v] = dis[u] + c;
pre[v] = i;
if (!vis[v])
q.push(v), vis[v] = 1;
}
}
}
return pre[ed] != -1;
}
void MCMF(int st, int ed, ll &cost)
{
cost = 0;
while (SPFA(st, ed))
{
int mi = INF;
for (int i = pre[ed]; ~i; i = pre[e[i ^ 1].v])
mi = min(mi, e[i].w);
for (int i = pre[ed]; ~i; i = pre[e[i ^ 1].v])
{
e[i].w -= mi, e[i ^ 1].w += mi;
cost += 1LL * mi * e[i].c;
}
}
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
memset(h, -1, sizeof(h));
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &b[i]);
if (a[i] > b[i]) //源点
{
AddEdge(0, i, a[i] - b[i], 0); //无代价权值差容量
AddEdge(i, 0, 0, 0);
}
else if (a[i] < b[i]) //汇点
{
AddEdge(i, n + 1, b[i] - a[i], 0);
AddEdge(n + 1, i, 0, 0);
}
}
for (int i = 1; i < n; ++i)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
AddEdge(u, v, INF, w);
AddEdge(v, u, 0, -w);
AddEdge(v, u, INF, w);
AddEdge(u, v, 0, -w);
}
ll cost = 0;
MCMF(0, n + 1, cost);
cout << cost << endl;
return 0;
}
G Checkpoints <tarjan>
如果能从起点到终点再回到起点则起点和终点在同一个强连通分量内。找出起点和终点的强连通分量。尝试暴力删除一些点。
如果删除再次Tarjan得到强连通分量,如果起点和终点依然在同一个强连通内则说明方案可行,并且标记当前节点下次不再处理。
因为此次处理会把所有包含删除当前点的状态都遍历到。
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 110;
int n, m, st, ed;
vector<int> e[N];
int dfn[N], low[N], num, idx; //节点编号 子树能到的最小编号 强连通分量数
bool vis[N], slv[N]; //栈内节点 已处理节点
int stk[N], tot;
int ans;
void Tarjan(int x, int *blo) //结果保存在blo内
{
dfn[x] = low[x] = ++idx;
stk[++tot] = x, vis[x] = 1;
for (int y : e[x])
if (!dfn[y])
Tarjan(y, blo), low[x] = min(low[x], low[y]);
else if (vis[y])
low[x] = min(low[x], low[y]);
if (low[x] == dfn[x])
{
int y;
++num;
do
{
y = stk[tot--], vis[y] = 0;
blo[y] = num;
} while (y != x);
}
}
void solve(int del) //删除了del节点后
{
int blo[N]; //强连通最小编号 每层独立
num = idx = 0;
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
Tarjan(st, blo); //从起点开始得到强连通分量
if (blo[st] != blo[ed]) //起点终点不是强连通则无法过去再返回
return ;
slv[del] = 1; //删除当前节点后依然满足则标记以后不再处理 因为接下来的递归会把包含删除当前节点的所有状态全部处理到
int cnt = 0; //和起点在同一强连通内的点数
for (int i = 1; i <= n; ++i)
if (blo[i] == blo[st])
++cnt;
ans = min(ans, cnt);
for (int i = 1; i <= n; ++i) //尝试删除每个点并递归处理
if (i != st && i != ed && !slv[i]) //不删除起点终点
{
vector<int> cp = e[i]; //拷贝当前节点的边
e[i].clear(); //并删除
solve(i); //递归处理
e[i] = cp; //还原
}
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int T;
cin >> T;
while (T--)
{
cin >> n >> m >> st >> ed;
for (int i = 1; i <= n; ++i)
e[i].clear();
for (int i = 0; i < m; ++i)
{
int u, v;
scanf("%d%d", &u, &v);
e[u].push_back(v);
}
ans = INF;
memset(slv, 0, sizeof(slv));
solve(0);
cout << ans - 2 << endl; //减去起点终点
}
return 0;
}