Overview:
ACdream 群【第三次原创群赛】将于【11月17日星期六晚19点】进行。本次原创群赛由[sjtu]ftiasch(人称 GXX 神牛、叉妹)出题。欢迎大家积极参与~【冠军】和【ACdreamOJ第10000次提交】的ID我们将会赠送小礼品一份 。。 比赛传送门。。。。
(。。。看样子叉娘不愿意在 blog 里贴太学术的东西。。。以下授权转发。。。
ac-dream-3rd.tar (题目、标程、解题报告、测试数据。。。
Official Solutions:
Xor
考虑第\(i\)个二进制位,因为\(n\)是奇数,所以\(A\)中第\(i\)位\(0\)和\(1\)的个数不相等(对于\(B\)也成立)。于是可以确定\(x\)的第\(i\)位,最后再验证\(x\)是否合法即可。
Triangle
由叉积的表达式,知道面积只跟坐标的奇偶性有关,枚举每个点的类型即可。
Tranform
广搜的复杂度是… \[N(1 + \frac{1}{2} + \cdots + \frac{1}{N}) = O(N \log N)\].
Product
\[v(A) = \frac{\prod_{a \in A}(1 + a) – \prod_{a \in A}(1 – a)}{2}\]
Permanent
状压动态规划。
Path
如果 \(x\) 是可行的,那么 \((x – 2)\) 也是可行的,所以可以动态规划求出最长的奇数和偶数。注意负数的情况。
#include <cassert>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000;
int n, q, parent[N];
int find(int i) {
return parent[i] == -1 ? i : (parent[i] = find(parent[i]));
}
int edge_count, first_edge[N], to[N << 1], length[N << 1], next_edge[N << 1];
const int INF = 100000000;
int down[N][2], limit[2];
void add_edge(int u, int v, int w) {
to[edge_count] = v;
length[edge_count] = w;
next_edge[edge_count] = first_edge[u];
first_edge[u] = edge_count ++;
}
#define UPDATE(x, a) x = max(x, a)
void dfs(int p, int u) {
down[u][0] = 0;
down[u][1] = -INF;
for (int iter = first_edge[u]; iter != -1; iter = next_edge[iter]) {
int v = to[iter];
int l = length[iter];
if (v != p) {
dfs(u, v);
for (int x = 0; x < 2; ++ x) {
for (int y = 0; y < 2; ++ y) {
UPDATE(limit[x + y + l & 1], down[u][x] + down[v][y] + l);
}
}
for (int y = 0; y < 2; ++ y) {
UPDATE(down[u][y + l & 1], down[v][y] + l);
}
}
}
}
int main() {
assert(scanf("%d%d", &n, &q) == 2);
assert(1 <= n && n <= N);
assert(1 <= q && q <= N);
memset(parent, -1, sizeof(parent));
edge_count = 0;
memset(first_edge, -1, sizeof(first_edge));
for (int i = 0; i < n - 1; ++ i) {
int a, b, c;
assert(scanf("%d%d%d", &a, &b, &c) == 3);
assert(1 <= a && a <= n);
assert(1 <= b && b <= n);
assert(1 <= c && c <= 2);
a --;
b --;
assert(find(a) != find(b));
parent[find(a)] = find(b);
add_edge(a, b, c);
add_edge(b, a, c);
}
limit[0] = limit[1] = -INF;
dfs(-1, 0);
while (q --) {
int l;
assert(scanf("%d", &l) == 1);
if (l < 0) {
puts("No");
} else {
int t = l % 2;
puts(l <= limit[t] ? "Yes" : "No");
}
}
return 0;
}
Multiplication
\[C[k] = (A[1] + A[2] + \cdots + A[k]) \cdot B[k] + (B[1] + B[2] + \cdots + B[k]) \cdot A[k] – A[k] \cdot B[k]\]
Matching
利用 Tutte matrix 可以得到很短的\(O(N^3)\)算法。
#include <cassert>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100;
const int MOD = 1000000000 + 7;
int n, a[N][N];
int inverse(int a) {
return a == 1 ? 1 : (long long)(MOD - MOD / a) * inverse(MOD % a) % MOD;
}
int main() {
assert(scanf("%d", &n) == 1);
assert(1 <= n && n <= N);
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
assert(scanf("%d", &a[i][j]) == 1);
}
}
for (int i = 0; i < n; ++ i) {
assert(a[i][i] == 0);
for (int j = 0; j < n; ++ j) {
assert(a[i][j] == a[j][i]);
assert(a[i][j] == 0 || a[i][j] == 1);
}
}
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
if (a[i][j]) {
a[i][j] = rand() % MOD;
a[j][i] = MOD - a[i][j];
}
}
}
int rank = 0;
for (int i = 0; i < n; ++ i) {
int pivot = rank;
while (pivot < n && !a[pivot][i]) {
pivot ++;
}
if (pivot < n) {
for (int j = 0; j < n; ++ j) {
swap(a[rank][j], a[pivot][j]);
}
{
int times = inverse(a[rank][i]);
for (int j = 0; j < n; ++ j) {
a[rank][j] = (long long)a[rank][j] * times % MOD;
}
for (int k = 0; k < n; ++ k) {
if (k != rank && a[k][i]) {
int times = a[k][i];
for (int j = 0; j < n; ++ j) {
(a[k][j] += MOD - (long long)a[rank][j] * times % MOD) %= MOD;
}
}
}
}
rank ++;
}
}
assert(!(rank & 1));
printf("%d\n", rank >> 1);
return 0;
}
/* .................................................................................................................................. */
const int N = 369;
int P[N], F[N], B[N], Q[N], head, tail;
bool G[N][N], InB[N], inQ[N]; int mark[N], tot;
int n, s, t, match;
#define pre F[P[i]]
int lca(int u, int v){
++tot; RST(mark);
for (int i=u;i;i=pre){ i=B[i]; mark[i]=tot; }
for (int i=v;i;i=pre){ i=B[i]; if (mark[i]==tot) return i;}
}
void blossoming(int u,int v){
int z = lca(u, v); RST(InB);
for (int i=u;B[i]!=z;i=pre){
if (B[pre]!=z) F[pre]=P[i];
InB[B[i]]=true;
InB[B[P[i]]]=true;
}
for (int i=v;B[i]!=z;i=pre){
if (B[pre]!=z) F[pre]=P[i]; //同理
InB[B[i]]=true;
InB[B[P[i]]]=true;
}
if (B[u]!=z) F[u]=v;
if (B[v]!=z) F[v]=u;
REP_1(i, n)
if (InB[B[i]]){
B[i]=z;
if (!inQ[i]){
Q[tail++]=i;
inQ[i]=true;
}
}
}
void change(){
int x,y,z=t; while (z){
y=F[z], x=P[y];
P[y]=z, P[z]=y, z=x;
}
}
bool bfs(){
RST(F, inQ); REP_1(i, n) B[i] = i;
head=0; tail=1, Q[0]=s, inQ[s]=1;
while (head < tail){
int u=Q[head++];
REP_1(v, n) if (G[u][v] && B[u]!=B[v] && P[u]!=v){
if (s==v || P[v] && F[P[v]]) blossoming(u,v);
else if (!F[v]){
F[v] = u; if (P[v]){
Q[tail++] = P[v];
inQ[P[v]] = true;
}
else{
t = v, change();
return true;
}
}
}
}
return false;
}
int EBC(){
int match = 0; RST(P); REP_1_N(s, n) if (!P[s] && bfs()) ++match;
return match;
}
void init(){
RD(n); REP_2_1(i, j, n, n) RD(G[i][j]);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
init(); OT(EBC());
}
Cut
一条边可以切开,当且仅当两边联通块都是偶数。
Component
考虑联通块不包括重心,则最多只能有\(\frac{n}{2}\)的大小。于是就算包含重心的联通块的最小权值,有一个熟知的动态规划算法可以做到\(O(N^2)\)。之后点分治即可。复杂度仍然是\(O(N^2)\)。
"熟知的动态规划算法"可以参见何森的集训队论文。
#include <cassert>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 2000;
const int INF = 1000000000;
int n, weight[N], parent[N], answer[N + 1];
vector <int> adjacent[N];
int find(int i) {
return parent[i] == -1 ? i : find(parent[i]);
}
bool mark[N];
int size[N], max_tree[N];
vector <int> nodes;
int minimum[N + 1][N + 1];
#define foreach(i, v) for (__typeof((v).begin()) i = (v).begin(); i != (v).end(); ++ i)
void dfs(int p, int u) {
size[u] = 1;
max_tree[u] = 0;
nodes.push_back(u);
foreach (iter, adjacent[u]) {
int v = *iter;
if (v != p && !mark[v]) {
dfs(u, v);
size[u] += size[v];
max_tree[u] = max(max_tree[u], size[v]);
}
}
}
void divide(int root) {
nodes.clear();
dfs(-1, root);
int total = size[root];
int new_root = -1;
int min_cost = INF;
foreach (iter, nodes) {
int u = *iter;
int ret = max(total - size[u], max_tree[u]);
if (ret < min_cost) {
new_root = u;
min_cost = ret;
}
}
nodes.clear();
dfs(-1, new_root);
int m = nodes.size();
minimum[m][0] = 0;
for (int i = 1; i <= m; ++ i) {
minimum[m][i] = INF;
}
for (int i = m - 1; i >= 0; -- i) {
int id = nodes[i];
for (int j = 0; j <= m; ++ j) {
minimum[i][j] = minimum[i + size[id]][j];
if (j) {
minimum[i][j] = min(minimum[i][j], minimum[i + 1][j - 1] + weight[id]);
}
}
}
for (int j = 1; j <= m; ++ j) {
answer[j] = min(answer[j], minimum[1][j - 1] + weight[new_root]);
}
mark[new_root] = true;
foreach (iter, adjacent[new_root]) {
int v = *iter;
if (!mark[v]) {
divide(v);
}
}
}
int main() {
assert(scanf("%d", &n) == 1);
assert(1 <= n && n <= N);
for (int i = 0; i < n; ++ i) {
assert(scanf("%d", weight + i) == 1);
assert(1 <= weight[i] && weight[i] <= 1000000);
}
memset(parent, -1, sizeof(parent));
for (int i = 0; i < n - 1; ++ i) {
int a, b;
assert(scanf("%d%d", &a, &b) == 2);
a --;
b --;
adjacent[a].push_back(b);
adjacent[b].push_back(a);
assert(find(a) != find(b));
parent[find(a)] = find(b);
}
for (int i = 1; i <= n; ++ i) {
answer[i] = INF;
}
memset(mark, 0, sizeof(mark));
divide(0);
for (int i = 1; i <= n; ++ i) {
printf("%d%c", answer[i], i == n ? '\n' : ' ' );
}
return 0;
}
/* .................................................................................................................................. */
const int N = 3009;
int A[N], f[N], dp[N][N];
VI adj[N]; bool vis[N]; int sz[N];
int n, res;
#define v adj[u][i]
void dfs(int u){
vis[u] = true, sz[u] = 1;
FLC(dp[u], 0x3f), dp[u][1] = A[u];
REP(i, SZ(adj[u])) if (!vis[v]){
dfs(v), sz[u] += sz[v];
DWN_1(j, sz[u], 2) DWN_1(k, min(sz[v], j-1), 1) // 剪枝 .. .
checkMin(dp[u][j], dp[u][j-k] + dp[v][k]);
}
REP_1(i, sz[u]) checkMin(f[i], dp[u][i]);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
REP_1_C(i, RD(n)) RD(A[i]);
FLC(f, 0x3f); DO(n-1){
int x, y; RD(x, y);
adj[x].PB(y), adj[y].PB(x);
}
dfs(1); REP_1(i, n-1) OT(f[i]);
cout << f[n] << endl;
}




Alca
Amber
Belleve Invis
Chensiting123
Edward_mj
Fotile96
Hlworld
Kuangbin
Liyaos
Lwins
LYPenny
Mato 完整版
Mikeni2006
Mzry
Nagatsuki
Neko13
Oneplus
Rukata
Seter
Sevenkplus
Sevenzero
Shirleycrow
Vfleaking
wangzhpp
Watashi
WJMZBMR
Wywcgs
XadillaX
Yangzhe
三途川玉子
About.me
Vijos
