2020-11-30 考试总结
作者:互联网
本来以为不会挂分了,但还是挂了45。。。本来不挂前面几名就稳了。
T1 ZZH的游戏
Description
ZZH 在和 GVZ 玩游戏。
ZZH 和 GVZ 各有一棵树,每棵树都有 \(n\) 个点。
两棵树上各自有一枚棋子。ZZH 的棋子初始在它树上的点 \(s\) ,GVZ 的棋子初始在树上的点 \(t\) 。
两人轮流操作。一个人操作时,可以选择不移动自己树上的棋子,也可以选择将自己树上的棋子移动到自己树上相邻的一个点。
当两枚棋子都在所在树的点 \(1\) 上时,游戏结束。游戏的分数是所有时刻中,两个棋子所在的点编号的和的最大值。
ZZH 和 GVZ 会合作让游戏结束时的分数尽量小。两人都极其聪明,因此两人都会采用最优策略。
ZZH 和 GVZ 一共会进行 \(T\) 次游戏,每次游戏的树与之前不同, \(n\) 也不一定相同,但两人的树的点数一定相等。
你需要求出在每一次游戏中,游戏结束时的分数的最小值。
\(\sum{n}\leq 10^6,T\leq 10^4\)
Solution
不难想到,可以二分答案,然后我们贪心得想的话,肯定时u先走到能走到的编号最小的点,然后v走到能走到的编号最小的点,然后u走到能走到的编号最小的点,v走到能走到的编号最小的点,如此循环往复,判断最后两个点是否都可以走到1即可。
考虑如何优化,不难看出我们可以对于每一条边 \((a,b)\) 按 \(\max(a,b)\) 排序,每次可以走的话就把 \(a,b\) 用并查集连起来。然后维护编号最小点即可。时间复杂度 \(\Theta(n\log^2 n)\)。
但是这样并不能过,考虑去掉二分,可以看出,每次当我们走不动的时候我们把答案+1即可。时间复杂度 \(\Theta(n\log n)\)。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 1000005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int tim,n,S,T;
struct Edge{
int u,v,w;
Edge(){}
Edge (int _u,int _v,int _w){u = _u,v = _v,w = _w;}
bool operator < (const Edge &p)const{return w < p.w;}
}e1[MAXN],e2[MAXN];
int lim,st[2],mi[2],miget1[MAXN],miget2[MAXN];
int fa1[MAXN],fa2[MAXN];
int findSet1 (int x){return fa1[x] == x ? x : fa1[x] = findSet1 (fa1[x]);}
int findSet2 (int x){return fa2[x] == x ? x : fa2[x] = findSet2 (fa2[x]);}
void unionSet1 (int u,int v){
int mi1 = miget1[findSet1 (u)],mi2 = miget1[findSet1 (v)];
fa1[findSet1 (u)] = findSet1 (v),
miget1[fa1[v]] = min (mi1,mi2);
}
void unionSet2 (int u,int v){
int mi1 = miget2[findSet2 (u)],mi2 = miget2[findSet2 (v)];
fa2[findSet2 (u)] = findSet2 (v),
miget2[fa2[v]] = min (mi1,mi2);
}
void modify (int now,int get){
if ((get == 0 && findSet1 (now) == findSet1 (S)))
mi[get] = min (mi[get],miget1[findSet1 (now)]);
else if (get == 1 && findSet2 (now) == findSet2 (T))
mi[get] = min (mi[get],miget2[findSet2 (now)]);
}
void Work (){
lim = S + T,st[0] = st[1] = 1,mi[0] = S,mi[1] = T;
for (Int i = 1;i <= n;++ i) fa1[i] = fa2[i] = miget1[i] = miget2[i] = i;
int las[2] = {1,1};
for (Int now = 0;now <= 4 * n;++ now){
int get = now & 1;
while (st[get] <= n - 1 && (get == 0 ? e1[st[get]].w : e2[st[get]].w) <= lim - mi[get ^ 1]){
if (get == 0) unionSet1 (e1[st[get]].u,e1[st[get]].v),modify (e1[st[get]].u,0),modify (e1[st[get]].v,0);
else unionSet2 (e2[st[get]].u,e2[st[get]].v),modify (e2[st[get]].u,1),modify (e2[st[get]].v,1);
st[get] ++;
}
if (findSet1 (1) == findSet1 (S) && findSet2 (1) == findSet2 (T)){
write (lim),putchar ('\n');
return ;
}
if (now & 1){
if (st[0] == las[0] && st[1] == las[1]) lim ++;
else las[0] = st[0],las[1] = st[1];
}
}
write (lim),putchar ('\n');
}
signed main(){
// freopen ("game.in","r",stdin);
// freopen ("game.out","w",stdout);
read (tim);
while (tim --> 0){
read (n);
for (Int i = 2,u,v;i <= n;++ i) read (u),read (v),e1[i - 1] = Edge (u,v,max (u,v));
for (Int i = 2,u,v;i <= n;++ i) read (u),read (v),e2[i - 1] = Edge (u,v,max (u,v));
read (S),read (T),sort (e1 + 1,e1 + n),sort (e2 + 1,e2 + n);
Work ();
}
return 0;
}
T2 ZZH与背包
Description
ZZH 有一个背包。
ZZH有 n 个物品,第 i 个物品的体积为 \(v_i\)。
ZZH 要去学校,她想带 n 个物品中的一些去学校。为了使背包不过于空,放入背包中的物品体积总和不能小于 l 。因为背包有容量上限,所以放入背包中的物品体积总和不能大于 r。
ZZH 想知道,她能带去学校的物品的集合一共有多少种。ZZH觉得这个问题太简单了,于是她把这个问题交给了你。
ZZH一共要去 q 次学校,因为ZZH还有一些必须带的东西(例如显卡),所以每次的 l,r 会改变。你需要对于每一个给出的 l,r 求出答案。
\(1\leq n \leq 40, q \leq 500, v_i \leq 10^9, 1 \leq l_i \leq r_i \leq \sum{v_i}\)
Solution
不难看出我们可以折半搜索,然后你每次直接从前往后扫一遍就好了。
时间复杂度 \(\Theta(q2^{n/2})\)。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 45
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int n,q,v[MAXN];
vector <int> get1,get2;
int my_lowerbound (vector <int> &S,int v){
int l = 0,r = S.size() - 1,ans = r + 1;
while (l <= r){
int mid = (l + r) >> 1;
if (S[mid] >= v) ans = mid,r = mid - 1;
else l = mid + 1;
}
return ans;
}
signed main(){
read (n),read (q);
int all = 1ll << n;
for (Int i = 1;i <= n;++ i) read (v[i]);
int len = (n <= 32 ? n / 3 : n / 2);
for (Int S = 0;S < (1ll << len);++ S){
int now = 0;
for (Int i = 1;i <= len;++ i) if (S >> i - 1 & 1) now += v[i];
get1.push_back (now);
}
sort (get1.begin(),get1.end());
len = n - len;
for (Int S = 0;S < (1ll << len);++ S){
int now = 0;
for (Int i = 1;i <= len;++ i) if (S >> i - 1 & 1) now += v[(n <= 32 ? n / 3 : n / 2) + i];
get2.push_back (now);
}
sort (get2.begin(),get2.end());
while (q --> 0){
int l,r,ans = 0;read (l),read (r);int ed = my_lowerbound (get2,l - get1[0]);
for (Int i = 0;i < get1.size() && get1[i] < l;++ i){
int now = get1[i];
while (ed && get2[ed - 1] >= l - now) -- ed;
ans += ed;
}
int L = 0,R = get1.size() - 1,res = 0;
while (L <= R){
int mid = (L + R) >> 1;
if (my_lowerbound (get2,r - get1[mid] + 1) == get2.size()) res = mid,L = mid + 1;
else R = mid - 1;
}
ed = get2.size();ed = my_lowerbound (get2,r - get1[0] + 1);
for (Int i = res;i < get1.size();++ i){
int now = get1[i];
while (ed && get2[ed - 1] >= r - now + 1) -- ed;
ans += get2.size() - ed;
}
write (all - ans),putchar ('\n');
}
return 0;
}
T3 ZZH与计数
Description
ZZH 喜欢计数。
ZZH 有很多的数,经过统计,ZZH一共有 \(v_0\) 个 0 ,\(v_1\) 个 1,...,\(v_{2^{n}-1}\) 个 \(2^{n} - 1\) 。因为一些原因,ZZH 只有这 \(2^{n}\) 种数。
ZZH 和 GVZ 要对这些数进行 m 次操作。每一次操作由一个人进行。每一次,有 p 的概率由 ZZH 操作, 1 - p 的概率由 GVZ 操作。
两人进行操作的时候都会依次操作每一个数。对于一个数 s ,如果 ZZH 对这个数进行操作,她会在 \(0,1,...,2^{n} - 1\) 中找出所有的 t,满足 t or s = s,然后将 s 等概率随机变成找出的 t 中的一个。
如果 GVZ 对这个数进行操作,她会在 \(0,1,...,2^{n} - 1\) 中找出所有的 t,满足 t and s = s ,然后将等概率随机变成找出的 t 中的一个。(这里的and/or指二进制与/或操作)
因为操作需要非常长的时间,她们想要知道所有操作结束后,对于每一个 i ,i 的个数的期望。因为期望值可能不是整数,所以她们想知道期望值模 的结果。
因为她们觉得这个问题太简单了,于是她们把这个问题交给了你。
Solution
还不会,先咕着。
话说本来以为这个题区分度会很大,不过似乎都是暴力。(为什么没人写30啊???
T4 ZZH的旅行
Description
给一棵有根树,\(1\) 为根。对于每个点 \(x\) ,求出对于满足如下条件的序列 \({s_1,...,s_k}\)
-
\(s_{i-1}\) 是 \(s_i\) 的祖先,且 $s_{i-1} != s_i $
-
\(s_1 = x\)
中,$ \sum_{i=2}^k(a_{s_{i-1}} - dis(s_{i-1},s_i))b_{s_i} $ 的最大值。
\(n \leq 10^6, 0 \leq a_i,b_i,d_i \leq 10^9\)
Solution
不难看出,设 \(f_u\) 为从 \(u\) 的答案,那么存在转移式:
\[f_u=\max_{v\in tree_u}\{f_v+(a_u+dep_u-dep_v)\times b_v\} \]然后你发现这个东西我们可以当成一个斜率为 \(b_v\),截距为 \(f_v-dep_v\times b_v\) 的直线在 \(x=a_u+dep_u\) 的取值。然后说白了你就是要维护一堆直线,然后问在 \(x\) 的取值的最高点。
于是问题就变成了李超线段树合并板子。
时间复杂度 \(\Theta(n\log n)\),空间动态开点回收空间之后可以做到 \(\Theta(n)\)。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define get yourmothersget
#define Int register int
#define int long long
#define MAXN 1000005
char buf[1<<17],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<17,stdin),p1==p2)?EOF:*p1++)
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int un,tmp[MAXN];
int n,tot,siz,K[MAXN],B[MAXN];
signed son[MAXN * 31][2],tree[MAXN * 31];
inline int f (int x,int l){return tmp[l] * K[x] + B[x];}
inline int newnode (){return ++ siz;}
inline void modify (signed &id,int l,int r,int x){
if (!id) id = newnode ();
if (l == r){
if (f (x,l) > f(tree[id],l)) tree[id] = x;
return ;
}
int mid = (l + r) >> 1;
if (K[tree[id]] < K[x]){
if (f (tree[id],mid) < f (x,mid)) modify (son[id][0],l,mid,tree[id]),tree[id] = x;
else modify (son[id][1],mid + 1,r,x);
}
else{
if (f (tree[id],mid) > f (x,mid)) modify (son[id][0],l,mid,x);
else modify (son[id][1],mid + 1,r,tree[id]),tree[id] = x;
}
}
inline int query (int id,int l,int r,int x){
if (!id) return 0;
if (l == r) return f (tree[id],x);
int mid = (l + r) >> 1;
if (x <= mid) return max (query (son[id][0],l,mid,x),f (tree[id],x));
else return max (query (son[id][1],mid + 1,r,x),f (tree[id],x));
}
inline void clear (int u){
son[u][0] = son[u][1] = tree[u] = 0;
}
inline void Merge (int l,int r,signed &u,int v){
if (!u || !v) return u = u + v,void ();
if (l == r){
if (f (tree[u],l) < f (tree[v],l)) tree[u] = tree[v];
if (v) clear (v);
return ;
}
int mid = (l + r) >> 1;
if (tree[v]) modify (u,l,r,tree[v]);
Merge (l,mid,son[u][0],son[v][0]);
Merge (mid + 1,r,son[u][1],son[v][1]);
if (v) clear(v);
}
struct Edge{
int v,w;
};
vector <Edge> G[MAXN];
#define son bitch
int a[MAXN],b[MAXN],dep[MAXN],get[MAXN],size[MAXN];
signed rt[MAXN],son[MAXN];
inline void dfs (int u,int fa){
size[u] = 1;
for (Edge to : G[u]) if (to.v ^ fa){
int v = to.v,w = to.w;
dep[v] = dep[u] + w,dfs (v,u),size[u] += size[v];
if (size[v] > size[son[u]]) son[u] = v;
}
}
inline void maintain (int u,int fa){
if (son[u]) maintain (son[u],u),rt[u] = rt[son[u]];
for (Edge to : G[u]) if (to.v != fa && to.v != son[u]) maintain (to.v,u),Merge (1,un,rt[u],rt[to.v]);
int id = lower_bound (tmp + 1,tmp + un + 1,a[u] + dep[u]) - tmp;
get[u] = query (rt[u],1,un,id),K[++ tot] = b[u],B[tot] = get[u] - dep[u] * b[u],modify (rt[u],1,un,tot);
}
signed main(){
read (n);
for (Int i = 1;i <= n;++ i) read (a[i]),read (b[i]);
for (Int i = 2,u,v,w;i <= n;++ i) read (u),read (v),read (w),G[u].push_back (Edge {v,w}),G[v].push_back (Edge {u,w});
dfs (1,0);for (Int i = 1;i <= n;++ i) tmp[i] = a[i] + dep[i];sort (tmp + 1,tmp + n + 1),un = unique (tmp + 1,tmp + n + 1) - tmp - 1;
maintain (1,0);for (Int i = 1;i <= n;++ i) write (get[i]),putchar ('\n');
return 0;
}
标签:11,int,30,mid,son,2020,ZZH,MAXN,id 来源: https://www.cnblogs.com/Dark-Romance/p/14063412.html