感觉自己被虐千百遍,看着final的队伍做题速度++++很难受。
最后只做了6题。
A: Self-Assembly
题目大意
C: Surely You Congest
题目大意
给出一个无向有权图,已知每个点会有一些汽车,这些汽车需要到1号点,汽车只会走最短路,每条路同时只能有一辆汽车从一个方向进入(也就是说不同时候是可以进入的,不同方向互不影响),车的速度一定,问有几辆车能到达1号点。
题解
显然首先从1开始跑一次最短路。然后我们可以构造出一个最短路DAG出来(把非最短路上的边删去)。对于同一条边不能同时同向进入的问题,对于到1号点距离相同的车,必然是同时同向进入这些边的(如果不是的话就存在更优的最短路),因此对于这些距离相同的点我们构造一个流网络,1为汇点,这些距离相同的点连到源点,边容量为1,跑一次最大流就知道这些车能过去几辆。另外显然对于同一条边,不同距离的车是不会同时同向经过这条边的(到这条边的距离一定不一样)。至于数据范围的问题,dinic跑DAG的复杂度其实挺低的。。然后给了9s不慌。
好久没有写过150行的代码了。。
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
#define rep(i,j,k) for(i=j;i<k;++i)
#define FORD(i,j,k) for(i=j;i>=k;--i)
const int inf = 0x7FFFFFFF;
typedef long long ll;
const int N = 25005, M = 150005;
struct Graph {
typedef pair<int, int> P;
int h[N], cur[N], p[M], from[M], dis[N], to[M], w[M], del[M], edge;
int s, t;
Graph() { reset(); }
void add(int a, int b, int c) {
p[++edge] = h[a]; from[edge] = a; to[edge] = b; w[edge] = c; h[a] = edge;
}
void addNet(int a, int b, int c) {
add(a, b, c);
add(b, a, 0);
}
void reset() {
memset(h, 0, sizeof h);
edge = 1;
}
void removeUnusedEdges() {
memset(del, 0, sizeof del);
for (int i = 2; i <= edge; ++i)
if (dis[to[i]] != dis[from[i]] + w[i])
del[i] = 1;
}
template<typename _Iterator>
void buildFrom(const Graph &g, _Iterator begin, _Iterator end) {
for (int i = 2; i <= g.edge; ++i) {
if (g.del[i]) continue;
addNet(g.to[i], g.from[i], 1);
}
for (_Iterator it = begin; it != end; ++it)
addNet(s, *it, 1);
}
void dijkstra(int s) {
priority_queue<P, vector<P>, greater<P>> pq;
memset(dis, 63, sizeof dis);
dis[s] = 0;
pq.push(P(dis[s], s));
while (!pq.empty()) {
P pa = pq.top(); pq.pop();
int u = pa.second;
if (pa.first > dis[u]) continue;
for (int i = h[u]; i; i = p[i]) {
if (dis[to[i]] > dis[u] + w[i]) {
dis[to[i]] = dis[u] + w[i];
pq.push(P(dis[to[i]], to[i]));
}
}
}
}
bool bfs() {
queue<int> q;
int i, x;
memset(dis, -1, sizeof dis);
q.push(s); dis[s] = 0;
while (!q.empty()) {
x = q.front(); q.pop();
for (i = h[x]; i; i = p[i])
if (w[i] && dis[to[i]] == -1) {
dis[to[i]] = dis[x] + 1;
q.push(to[i]);
}
}
return dis[t] != -1;
}
int dfs(int x, int low) {
int i, tmp, res = 0;
if (x == t) return low;
for (i = cur[x]; i && res < low; i = p[i])
if (w[i] && dis[to[i]] == dis[x] + 1) {
tmp = dfs(to[i], min(low - res, w[i]));
w[i] -= tmp; w[i ^ 1] += tmp; res += tmp;
if (w[i]) cur[x] = i;
}
if (!res) dis[x] = -1;
return res;
}
int dinic() {
int ans = 0, i;
while (bfs()) {
memcpy(cur, h, sizeof h);
ans += dfs(s, inf);
}
return ans;
}
} root, g;
int c[N];
int comp(int x, int y) {
return root.dis[x] < root.dis[y];
}
int main() {
int n, m, k, x, y, l, t, ans = 0, i;
scanf("%d%d%d", &n, &m, &k);
rep(i,0,m) {
scanf("%d%d%d", &x, &y, &t);
root.add(x, y, t);
root.add(y, x, t);
}
rep(i,0,k) scanf("%d", &c[i]);
root.dijkstra(1);
root.removeUnusedEdges();
sort(c, c + k, comp);
l = 0;
FOR(i,1,k) {
if (i != k && root.dis[c[i]] == root.dis[c[i - 1]])
continue;
if (i == l + 1) {
++ans;
} else {
g.reset();
g.s = 0; g.t = 1;
g.buildFrom(root, c + l, c + i);
ans += g.dinic();
}
l = i;
}
printf("%d\n", ans);
return 0;
}
D: Factors
题目大意
令 f(k) f ( k ) 表示k在质因数分解后的排列数,比如 f(20)=3 f ( 20 ) = 3 ,因为 20=2×2×5=2×5×2=5×2×2 20 = 2 × 2 × 5 = 2 × 5 × 2 = 5 × 2 × 2 。已知 n n ,求最小的使得 f(k)=n f ( k ) = n 。
题解
考虑函数
f
f
的递推情况,首先可以肯定的是我们总是使用尽量小的质数去表示,假设我们现在已经用前
m−1
m
−
1
小的质数
p1..m
p
1..
m
表示了
k
k
,幂为,令
k′=k×pam+1m+1
k
′
=
k
×
p
m
+
1
a
m
+
1
,首先可以知道的是:
令 s=∑mi=1ai s = ∑ i = 1 m a i
那么有:
因为题目已经限制 s,ai<64 s , a i < 64 ,因此组合数可以预处理出来,那么 f(k) f ( k ) 就可以暴力求解,状态数不是很多。
遇到了坑。。无符号乘上有符号变成有符号整数。。
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
#define rep(i,j,k) for(i=j;i<k;++i)
#define FORD(i,j,k) for(i=j;i>=k;--i)
typedef unsigned long long ull;
const ull inf = 1ull << 63;
ull p[] = {1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67};
map<ull, ull> ans;
ull C[64][64];
void calcC() {
int i, j;
C[0][0] = 1;
rep(i,1,64) {
C[i][0] = 1;
rep(j,1,64) {
if (C[i - 1][j - 1] < inf && C[i - 1][j] < inf)
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
else
C[i][j] = inf;
}
}
}
void calc(ull k, ull n, int m, int s) {
if (sum && (!ans.count(n) || ans[n] > k))
ans[n] = k;
ull k2 = k;
for (int a = 1; p[m + 1] <= inf / k2; ++a) {
k2 *= p[m + 1];
calc(k2, n * C[s + a][s], m + 1, s + a);
}
}
int main() {
ull n;
calcC();
calc(1, 1, 0, 0);
while (scanf("%llu", &n) == 1) {
printf("%llu %llu\n", n, ans[n]);
}
return 0;
}
F: Low Power
题目大意
题解
二分
H: Matryoshka
题目大意
题解
巨恶心的dp,队友做的。。
J: Pollution Solution
题目大意
求一个多边形与半圆的面积交。
题解
半圆和圆没啥区别,所以当圆看就行了。
先将多边形分解为多个三角形,一个点在圆心,那么三角形与圆有4种情况,分别是两个点在圆内(直接算三角形面积),两个点在圆外(算扇形面积),一个点在圆外一个点在圆内(拆成一个三角形和一个扇形),两个点在圆外(但与圆交于2点,拆成2扇形和1个三角形)。
代码就不放了。。模板题。网上模板满天飞。