CCPC-Wannafly Winter Camp Day3 (Div2, onsite)

Replay


Dup4:

  • 没想清楚就动手写? 写了两百行发现没用?想的还是不够仔细啊。
  • 要有莽一莽的精神

X:

  • 感觉今天没啥输出啊, 就推了个公式?抄了个板子, 然后就一直自闭A。
  • 语文差,题目没理解,导致写了接近三小时的A吧, 最后看了一眼群, 发现自己理解错了。
  • 以及感觉自己最近不会交流,有点毒瘤了。

A:二十四点*

Solved.

思路:

Div2 暴力跑?

 #include<bits/stdc++.h>

 using namespace std;

 int n;
double arr[]; int main()
{
while(~scanf("%d", &n))
{
for(int i = ; i <= n; ++i) scanf("%lf", arr + i);
else if(n == ) puts("");
else if(n == ) puts(""); }
return ;
}
 #include<bits/stdc++.h>
using namespace std; typedef long long ll;
const ll MOD = ;
const double eps = 1e-; int n, ans;
double arr[];
double brr[]; int DFS(int len)
{
if (len == )
{
if (fabs(brr[] - 24.0) < eps) return ;
else return ;
} for (int i = ; i <= len; ++i)
{
for (int j = i + ; j <= len; ++j)
{
double a = brr[i], b = brr[j], c = brr[len];
brr[j] = c;
double tmp = a + b;
brr[i] = tmp;
if (fabs(tmp - 24.0) < eps) return ;
if (DFS(len - )) return ; tmp = a - b;
brr[i] = tmp;
if (fabs(tmp - 24.0) < eps) return ;
if (DFS(len - )) return ; tmp = b - a;
brr[i] = tmp;
if (fabs(tmp - 24.0) < eps) return ;
if (DFS(len - )) return ; tmp = a * b;
brr[i] = tmp;
if (fabs(tmp - 24.0) < eps) return ;
if (DFS(len - )) return ; tmp = a / b;
brr[i] = tmp;
if (fabs(tmp - 24.0) < eps) return ;
if (DFS(len - )) return ; tmp = b / a;
brr[i] = tmp;
if (fabs(tmp - 24.0) < eps) return ;
if (DFS(len - )) return ; brr[i] = a, brr[j] = b, brr[len] = c;
}
} return ;
} void RUN()
{
while (~scanf("%d", &n))
{
for (int i = ; i <= n; ++i) scanf("%lf", arr + i);
ans = ;
for (int i = ; i < ( << n); ++i)
{
int tot = ;
for (int j = ; j < n; ++j)
{
if (i & ( << j))
{
brr[++tot] = arr[j + ];
}
}
int tmp = DFS(tot);
ans += tmp;
}
printf("%d\n", ans);
}
} int main()
{
RUN();
return ;
}

D:精简改良

Upsolved.

生成树状压DP

$dp[S][u] 表示点集为S, 根节点为u的最大贡献$

$dp[S][u] = max(dp[T][v] + dp[S - T][u] + dist[u][v] \cdot |T| \cdot |n - T|)$

$T表示点集为T 并且根节点为v的子树,并且T是S的子集$

$要注意有些非法的状态是不能够参与转移的$

$时间复杂度O(3^n \cdot n^2)$

$对于每一个集合枚举其子集  相当于 \sum\limits_{0}^{n} C_n^i \cdot 2^i = (1 + 2)^n = 3^n$

但是这里过不去 要卡点常 喵喵喵?  预处理了一下子集中有哪些点

 #include <bits/stdc++.h>
using namespace std; #define ll long long
#define INFLL 0x3f3f3f3f3f3f3f3f
#define N 110
int n, m;
int dist[][];
ll f[ << ][]; vector <int> vec[ << ];
int sze[ << ]; int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
memset(dist, -, sizeof dist);
memset(f, -, sizeof f);
for (int S = , len = ( << n); S < len; ++S)
{
for (int i = ; i < n; ++i) if ((S >> i) & )
vec[S].push_back(i + );
sze[S] = vec[S].size();
if (sze[S] == )
f[S][*vec[S].begin()] = ;
}
for (int i = , u, v, w; i <= m; ++i)
{
scanf("%d%d%d", &u, &v, &w);
dist[u][v] = dist[v][u] = w;
}
ll res = ;
for (int S = , len = ( << n); S < len; ++S)
{
for (auto u : vec[S])
{
for (int T = (S - ) & S; T != ; T = (T - ) & S) // 枚举子集
{
for (auto v : vec[T])
{
if (dist[u][v] == - || f[T][v] == - || f[S - T][u] == -) continue;
f[S][u] = max(f[S][u], f[T][v] + f[S - T][u] + 1ll * dist[u][v] * sze[T] * (n - sze[T]));
}
}
if (S == ( << n) - )res = max(res, f[S][u]);
}
}
printf("%lld\n", res);
}
return ;
}

E:最大独立集

Upsolved.

树上独立集的贪心做法:

每次取出叶子节点加入答案,然后将其父节点扔掉,循环操作,直至没有节点

那么考虑这一道题的操作,对于一棵树$T(k_i)来说$

它的每一个节点下面都连着一棵独立的树

那么这些独立的树都可以分别贪心求最大独立集

再考虑这些独立的树做完之后剩下的$T(k_i)$

如果这些独立的树的最大独立集需要取到其根节点,那么$T(k_i)中所有节点都不能取$

否则$T(k_i)的贡献就是以k_i为根的最大独立集$

这时候需要预处理出以$x \in [1, n] 为根的最大独立集中是否需要用到x$

树形dp

第一次dp ,$f[i] 表示只考虑i的子树当前点 取不取$

显然,对于所有叶子节点都是取的 ,$f[i] = 1$

那么对于非叶子节点 $f[i] = 1 当且仅当其所有儿子的f[i] = 0$

第二次dp $g[i] 表示不考虑i的子树当前点取不取$

显然,$g[1] = 1 表示根节点不考虑其子树是取的$

再考虑其他点$u,如果u是不取的,当且仅当 g[fa[u]] = 1 并且 其父亲除它$

$以外的儿子中的f[i]都是不取的$

 #include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 200010
const ll MOD = (ll);
int n, m; ll base;
vector <int> G[N]; int fa[N], cnt[N], need[N];
// 0 get > 0 not get for cnt
void DFS(int u)
{
cnt[u] = ;
for (auto v : G[u]) if (v != fa[u])
{
fa[v] = u;
DFS(v);
if (cnt[v] == ) ++cnt[u];
}
if (cnt[u] == ) ++base;
} // need 0 get 1 not get
void DFS2(int u)
{
if (u != )
{
if (need[fa[u]] == && cnt[fa[u]] - (cnt[u] == ) == ) need[u] = ;
else need[u] = ;
}
for (auto v : G[u]) if (v != fa[u])
DFS2(v);
} int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
base = ;
for (int i = ; i <= n; ++i) G[i].clear();
for (int i = , u, v; i < n; ++i)
{
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
DFS();
need[] = ;
DFS2();
for (int i = ; i <= n; ++i)
{
if (cnt[i] == && need[i] == ) need[i] = ;
else need[i] = ;
}
ll res = base;
int vis = need[];
for (int i = , x; i <= m; ++i)
{
scanf("%d", &x);
printf("%lld\n", res);
res = (res * n) % MOD;
if (vis == ) res = (res + base) % MOD, vis = need[x];
else vis = ;
}
printf("%lld\n", res);
}
return ;
}

F:小清新数论*

Solved.

思路:

$ans = \sum\limits_{d = 1}^n \cdot \mu(d) \sum\limits_{i = 1}^{\frac{n}{d}} \sum\limits_{j = 1}^{\frac{n}{d}} [gcd(i, j) == 1]$

 #include<bits/stdc++.h>

 using namespace std;

 typedef long long ll;

 const ll MOD = ;
const int maxn = 1e7 + ; bool check[maxn];
int prime[maxn];
int mu[maxn];
ll sum[maxn]; void Moblus()
{
mu[] = ;
int tot = ;
for(int i = ; i < maxn; ++i)
{
if(!check[i])
{
prime[tot++] = i;
mu[i] = -;
}
for(int j = ; j < tot; ++j)
{
if(i * prime[j] > maxn) break;
check[i * prime[j]] = true;
if(i % prime[j] == )
{
mu[i * prime[j]] = ;
break;
}
else
{
mu[i * prime[j]] = -mu[i];
}
}
}
for(int i = ; i < maxn; ++i)
{
sum[i] = (sum[i - ] + mu[i]) % MOD;
}
} ll solve(int n, int m)
{
ll ans = ;
if(n > m) swap(n, m);
for(int i = , la = ; i <= n; i = la + )
{
la = min(n / (n / i), m / (m / i));
ans += (sum[la] - sum[i - ]) * (n / i) * (n / i);
}
return ans;
} int n; int main()
{
Moblus();
while(~scanf("%d", &n))
{
ll ans = ;
for(int i = ; i <= n; ++i)
{
ans = (ans + mu[i] * solve(n / i, n / i) % MOD + MOD) % MOD;
}
printf("%lld\n", ans);
}
return ;
}

G:排列

Solved.

签到。

 #include <bits/stdc++.h>
using namespace std; #define N 100010
int n, q[N], p[N], vis[N]; int main()
{
while (scanf("%d", &n) != EOF)
{
memset(vis, , sizeof vis);
memset(p, , sizeof p);
for (int i = ; i <= n; ++i) scanf("%d", q + i);
int j = ;
for (int i = ; i <= n; ++i)
{
if (i == || q[i] < q[i - ])
{
vis[j] = ;
p[q[i]] = j;
++j;
}
}
for (int i = ; i <= n; ++i) if (!p[i])
p[i] = j++;
for (int i = ; i <= n; ++i) printf("%d%c", p[i], " \n"[i == n]);
}
return ;
}

H:涂鸦*

Solved.

思路:

对于某一个点来说,它被染成黑点的概率是$\frac{(x - l + 1) \cdot (r - x + 1) \cdot 2}{(r - l + 1) \cdot (r - l + 2)}$

然后二维BIT维护矩形,最后将所有未被矩形覆盖的期望加起来即可

 #include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 1010
const ll p = (ll);
const ll MOD = (ll);
int n, m, q;
ll inv[N];
int ans[N][N]; namespace BIT
{
int a[N][N];
void init() { memset(a, , sizeof a); }
void update(int x, int y, int v)
{
for (int i = x; i > ; i -= i & -i)
for (int j = y; j > ; j -= j & -j)
a[i][j] += v;
}
int query(int x, int y)
{
int res = ;
for (int i = x; i < N; i += i & -i)
for (int j = y; j < N; j += j & -j)
res += a[i][j];
return res;
}
void update(int x1, int y1, int x2, int y2)
{
update(x1 - , y1 - , );
update(x1 - , y2, -);
update(x2, y1 - , -);
update(x2, y2, );
}
} int main()
{
inv[] = ;
for (int i = ; i < N; ++i) inv[i] = inv[p % i] * (p - p / i) % p;
while (scanf("%d%d%d", &n, &m, &q) != EOF)
{
memset(ans, , sizeof ans); BIT::init();
for (int i = , l, r; i <= n; ++i)
{
scanf("%d%d", &l, &r);
for (int j = l; j <= r; ++j)
ans[i][j] = 2ll * (j - l + ) %p * (r - j + ) %p * inv[r - l + ] % p * inv[r - l + ] % p;
//cout << ans[i][l] * (r - l + 1) * (r - l + 2) / 2 % p << endl;
}
for (int qq = ; qq <= q; ++qq)
{
int x1, x2, y1, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
BIT::update(x1, y1, x2, y2);
}
ll res = ;
for (int i = ; i <= n; ++i) for (int j = ; j <= m; ++j) if (!BIT::query(i, j))
{
//cout << i << " " << j << endl;
res = (res + ans[i][j]) % p;
}
printf("%lld\n", res);
}
return ;
}

I:石头剪刀布

Solved.

思路:

考虑有$x之前要被a个人挑战,要挑战b个人$

那么$此时对于x满足的方案数是3^n \cdot \frac{1}{3}^b \cdot \frac{2}{3}^a$

然后考虑怎么维护$a和b$

我们发现对于1操作

我们将$y 接在 x 下面的话,这样最后会构成一棵树$

那么$b就是祖宗的个数$

再考虑$a是什么,我们发现其实就是 它的所有祖先当中 所有比它后加入的儿子个数 以及它所有的儿子个数$

然后再发现一棵子树对其他点的影响,发现受影响的点就是它的父亲中比它先来的儿子中所对应的所有子树

这一段在$DFS序中是连续的,线段树维护即可$

刚开始错误思路,写了个树剖,喵喵喵?

 #include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 200010
const ll MOD = (ll);
int n, m, fa[N];
vector <int> G[N];
struct qnode
{
int op, x, y;
void scan()
{
scanf("%d%d", &op, &x); ++x;
if (op == )
{
scanf("%d", &y); ++y;
fa[y] = x;
G[x].push_back(y);
}
}
}q[N]; int deep[N], sze[N], top[N], son[N], lp[N], rp[N], fp[N], cnt;
void DFS(int u)
{
sze[u] = ;
for (auto v : G[u]) if (v != fa[u])
{
deep[v] = deep[u] + ;
DFS(v);
sze[u] += sze[v];
if (!son[u] && sze[v] > sze[son[u]]) son[u] = v;
}
} void getpos(int u, int sp)
{
top[u] = sp;
lp[u] = ++cnt;
fp[cnt] = u;
if (!son[u])
{
rp[u] = cnt;
return;
}
getpos(son[u], sp);
for (auto v : G[u]) if (v != fa[u] && v != son[u])
getpos(v, v);
rp[u] = cnt;
} namespace SEG
{
struct node
{
int v[], lazy[];
node() {}
void init()
{
memset(v, , sizeof v);
memset(lazy, , sizeof lazy);
}
node operator + (const node &other) const
{
node res; res.init();
for (int i = ; i < ; ++i) res.v[i] = v[i] + other.v[i];
return res;
}
}a[N << ], res;
void build(int id, int l, int r)
{
a[id].init();
if (l == r)
{
a[id].v[] = deep[fp[l]];
return;
}
int mid = (l + r) >> ;
build(id << , l, mid);
build(id << | , mid + , r);
}
void pushdown(int id)
{
for (int i = ; i < ; ++i) if (a[id].lazy[i])
{
int lazy = a[id].lazy[i];
a[id].lazy[i] = ;
a[id << ].v[i] += lazy;
a[id << | ].v[i] += lazy;
a[id << ].lazy[i] += lazy;
a[id << | ].lazy[i] += lazy;
}
}
void update(int id, int l, int r, int ql, int qr, int v, int vis)
{
if (l >= ql && r <= qr)
{
a[id].v[vis] += v;
a[id].lazy[vis] += v;
return;
}
int mid = (l + r) >> ;
pushdown(id);
if (ql <= mid) update(id << , l, mid, ql, qr, v, vis);
if (qr > mid) update(id << | , mid + , r, ql, qr, v, vis);
}
void query(int id, int l, int r, int pos)
{
if (l == r)
{
res = a[id];
return;
}
int mid = (l + r) >> ;
pushdown(id);
if (pos <= mid) query(id << , l, mid, pos);
else query(id << | , mid + ,r , pos);
}
} ll qmod(ll base, ll n)
{
ll res = ;
while (n)
{
if (n & ) res = (res * base) % MOD;
base = base * base % MOD;
n >>= ;
}
return res;
}
ll ans[N]; int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
++n;
for (int i = ; i <= n; ++i) G[i].clear();
memset(fa, , sizeof fa);
memset(son, , sizeof son); cnt = ;
for (int i = ; i <= m; ++i) q[i].scan();
for (int i = ; i <= n; ++i) if (!fa[i])
{
fa[i] = ;
G[].push_back(i);
}
deep[] = -;
DFS(); getpos(, );
SEG::build(, , n);
for (int i = ; i <= n; ++i) if (fa[i] != )
SEG::update(, , n, lp[fa[i]], lp[i] - , , );
for (int i = m; i >= ; --i)
{
if (q[i].op == )
{
SEG::res.init();
SEG::query(, , n, lp[q[i].x]);
int x = SEG::res.v[], y = SEG::res.v[];
ans[i] = qmod(, n - x - y - ) * qmod(, y) % MOD;
}
else
{
SEG::res.init();
SEG::query(, , n, lp[q[i].y]);
int x = SEG::res.v[];
SEG::update(, , n, lp[q[i].y], rp[q[i].y], -x, );
SEG::update(, , n, lp[q[i].x], lp[q[i].y] - , -, );
}
}
for (int i = ; i <= m; ++i) if (q[i].op == ) printf("%lld\n", ans[i]);
}
return ;
}

并查集

按秩合并(为什么路径压缩不可以呢, 据说是因为破坏了树结构?)

每次对于$x$, $y$两个集合

当把$y$集合合并到$x$集合中时:

  • 使得$y$上的总比赛场次增量减去$x$原本的总比赛场次增量, 同时使得$x$总比赛场次增量增加
  • 使得$x$总主场场次增量增加, 同时使得$y$上的总主场场次增量减去$x$现在的总主场场次增量

当把$x$集合合并到$y$集合中时:

  • 使得$x$上的总比赛场次增量减去$y$原本的总比赛场次增量, 同时使得$y$总比赛场次增量增加
  • 使得$x$上的总主场场次增量减去$y$原本的总主场场次增量, 同时是的$x$总主场场次增量增加

在寻找根节点时, 统计路径上的总主场场次增量$a$以及总主场场次增量$b$

那么此时方案数为$3^n\cdot2^a\cdot(\frac{1}{3})^b$

 #include<bits/stdc++.h>

 using namespace std;

 typedef long long ll;

 const ll MOD = ;
const int maxn = 2e5 + ; ll qpow(ll x, ll n)
{
assert(n >= );
ll res = ;
while (n)
{
if (n & ) res = res * x % MOD;
x = x * x % MOD;
n >>= ;
}
return res;
} struct node {
int father;
int dw;
int dv;
node() {}
node(int father, int dw, int dv) :father(father), dw(dw), dv(dv) {}
}; int n, m;
int fa[maxn];
int rk[maxn];
int w[maxn], v[maxn];//sum 主
int dw[maxn], dv[maxn];
int tot; void Init()
{
tot = n + ;
for (int i = ; i < maxn; ++i) fa[i] = i, w[i] = , v[i] = , rk[i] = ;
} node find(int x)
{
if (x == fa[x]) return node(fa[x], dw[x], dv[x]);
node temp = find(fa[x]);
return node(temp.father, temp.dw + dw[x], temp.dv + dv[x]);
} void Mix(int x, int y)
{
x = find(x).father, y = find(y).father;
if (rk[x] >= rk[y])
{
rk[x] = max(rk[x], rk[y] + );
fa[y] = x; dw[y] -= dw[x];
dw[x]++;
dv[x]++;
dv[y] -= dv[x];
}
else
{
rk[y] = max(rk[y], rk[x] + );
fa[x] = y; dw[x] -= dw[y];
dw[y]++;
dv[x]++;
dv[x] -= dv[y];
}
} void RUN()
{
while (~scanf("%d %d", &n, &m))
{
Init();
for (int q = , op, x, y; q <= m; ++q)
{
scanf("%d", &op);
if (op == )
{
scanf("%d %d", &x, &y);
Mix(x, y);
}
else if (op == )
{
scanf("%d", &x);
node tmp = find(x);
ll ans = qpow(, n) * qpow((qpow(, tmp.dw)), MOD - ) % MOD * qpow(, tmp.dv) % MOD;
printf("%lld\n", ans);
}
}
}
} int main()
{
#ifdef LOCAL_JUDGE
freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE RUN(); #ifdef LOCAL_JUDGE
fclose(stdin);
#endif // LOCAL_JUDGEf return ;
}
上一篇:【JDBC核心】操作 BLOB 类型字段


下一篇:为什么报错说req未定义,createServer只接受匿名函数吗?