预期 | 一测 | ||
a | 100 | 100 | 0 |
b | 100 | 100 | 0 |
c | 100 | 100 | 0 |
d | 100 | 0 | -100 |
总分 | 400 | 300 | -100 |
问题 A: 祖孙询问
树链剖分板题。
#include<bits/stdc++.h>
#define N 40009
#define LL long long
using namespace std;
template<typename T>inline void read(T &x){
T ch=getchar(),xx=1;x=0;
while(!isdigit(ch)) xx=ch=='-' ? -1 : xx,ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
x*=xx;
}
template<typename T>inline void print(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,cnt,m;
int head[N],to[N<<1],nxt[N<<1],root;
int dep[N];
int fa[N],top[N],siz[N],son[N];
void add(int u,int v){
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs1(int u,int h){
dep[u]=dep[h]+1;
siz[u]=1;
fa[u]=h;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==h) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
}
void dfs2(int x){
top[x]=x==son[fa[x]] ? top[fa[x]] : x;
if(son[x])
dfs2(son[x]);
for(int i=head[x];i;i=nxt[i]){
int v=to[i];
if(!top[v])
dfs2(v);
}
}
int get(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])
swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y] ? x : y;
}
int main(){
read(n);
for(int i=1;i<=n;i++){
int u,v;
read(u),read(v);
if(v==-1) root=u;
else add(u,v),add(v,u);
}
dfs1(root,0);
top[root]=root;
dfs2(root);
read(m);
for(int i=1;i<=m;i++){
int u,v;
read(u),read(v);
int k=get(u,v);
if(k==u) puts("1");
else if(k==v) puts("2");
else puts("0");
}
return 0;
}
问题 B: 软件包管理器
树链剖分建树后,对于安装一层一层向上跳并将相应区间变为 1 ,计算前后区间和大小差,对于卸载,由于树链剖分的性质,以一个结点为根的子树所在映射的线段树区间就是该结点所在位置到加上这棵子树的大小后所在的位置,影响的数量就是这个区间的和,然后将这个区间变为零就行了。
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
const int MAXN = 100009;
struct node{
int l, r, lazy, sum;
}tr[MAXN * 4];
int n, m, ans, cnt, tot;
int head[MAXN], to[MAXN * 2], nxt[MAXN * 2];
int in[MAXN];
int dep[MAXN], fa[MAXN], size[MAXN], son[MAXN], top[MAXN], seg[MAXN], rev[MAXN];
int v[MAXN], mx[MAXN];
char s[15];
inline void add_(int u, int v){
to[++cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt;
}
inline void dfs(int u, int f){
dep[u] = dep[f] + 1;
fa[u] = f;
size[u] = 1;
for(int i = head[u]; i; i = nxt[i]){
int v = to[i];
if(v != f){
dfs(v, u);
size[u] += size[v];
if(size[v] > size[son[u]])
son[u] = v;
}
}
}
inline void dfs2(int u, int f){
mx[u] = seg[u];
if(son[u]){
seg[son[u]] = ++tot;
rev[tot] = son[u];
top[son[u]] = top[u];
dfs2(son[u], u);
mx[u] = max(mx[u], mx[son[u]]);
}
for(int i = head[u]; i; i = nxt[i]){
int v = to[i];
if(!top[v]){
seg[v] = ++tot;
rev[tot] = v;
top[v] = v;
dfs2(v, u);
mx[u] = max(mx[u], mx[v]);
}
}
}
inline void build(int x, int l, int r){
tr[x].l = l, tr[x].r = r;
tr[x].lazy = tr[x].sum = 0;
if(l == r)
return;
int mid = (l + r)>>1;
build(lc, l, mid);
build(rc, mid + 1, r);
}
inline void pushdown(int x){
if(tr[x].lazy == -1)
return;
tr[lc].lazy = tr[x].lazy;
tr[rc].lazy = tr[x].lazy;
tr[x].sum = (ll)tr[x].lazy * (tr[x].r - tr[x].l + 1);
tr[x].lazy = -1;
}
inline void update(int x, int l, int r, int val){
if(tr[x].l > r || tr[x].r < l)
return;
if(tr[x].l >= l && tr[x].r <= r){
tr[x].lazy = val;
pushdown(x);
return;
}
pushdown(x);
update(lc, l, r, val);
update(rc, l, r, val);
pushdown(lc), pushdown(rc);
tr[x].sum = tr[lc].sum + tr[rc].sum;
}
inline ll query(int x, int l, int r){
if(tr[x].l > r || tr[x].r < l)
return 0LL;
if(tr[x].l >= l && tr[x].r <= r){
pushdown(x);
return tr[x].sum;
}
pushdown(x);
ll tmp1 = query(lc, l, r), tmp2 = query(rc, l, r);
pushdown(lc), pushdown(rc);
tr[x].sum = tr[lc].sum + tr[rc].sum;
return tmp1 + tmp2;
}
inline void ins(int u){
while(!in[u] && u){
// cout<<u<<" ";
int tmp = query(1, seg[top[u]], seg[u]);
// cout<<tmp<<endl;
ans += seg[u] - seg[top[u]] + 1 - tmp;
update(1, seg[top[u]], seg[u], 1);
if(tmp)
break;
u = fa[top[u]];
}
}
inline void del(int u){
int tmp = query(1, seg[u], mx[u]);
// cout<<"del : "<<seg[u]<<" "<<mx[u]<<endl;
update(1, seg[u], mx[u], 0);
ans = tmp;
}
int main(){
scanf("%d", &n);
for(int i = 2; i <= n; i++){
int a;
scanf("%d", &a);
a++;
add_(a, i);
add_(i, a);
}
// dep[1] = 1;
dfs(1, 0);
seg[1] = rev[1] = top[1] = ++tot;
dfs2(1, 0);
build(1, 1, n);
scanf("%d", &m);//cout<<m<<" ";
while(m--){
int x;
ans = 0;
scanf("%s%d", s, &x);
x++;
if(s[0] == 'i')
ins(x);
if(s[0] == 'u')
del(x);
printf("%d\n", ans);
}
return 0;
}
/*
7
0 0 0 1 1 5
5
install 5
install 6
uninstall 1
install 4
uninstall 0
*/
问题 C: Best Cow Fences
二分答案,判定“是否存在一个长度不小于L的子段,平均数不小于二分的值”。
把数列中每个数都减去二分的值,就转化为判定“是否存在一个长度不小于L的子段,字段和非负”。
用 sum 数组表示前缀和,求 ,随着 i 的增加,每次只有一个值
也就是 有可能更新 。
#include<bits/stdc++.h>
using namespace std;
#define N 100001
double a[N],b[N],sum[N],le,ri;
int n,l;
int main(){
scanf("%d%d",&n,&l);
for (int i=1;i<=n;++i)
scanf("%lf",a+i);
double eps=1e-5;
le=-1e6,ri=1e6;
while(ri-le>eps){
double mid=(le+ri)/2;
for(int i=1;i<=n;i++) b[i]=a[i]-mid;
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+b[i];
double ans=-1e10,val=1e10;
for(int i=l;i<=n;i++){
val=min(val,sum[i-l]);
ans=max(ans,sum[i]-val);
}
if(ans>=0) le=mid;
else ri=mid;
}
printf("%d",int(ri*1000));
}
问题 D: 数列极差
这个题如果拿出测试案例1 2 3 这三个数有三种不同的操作顺序从中我们容易发现如果要求最大值的话,那么每次先用最小的两个数,相反,如果要求最小值的话,每次先用最大的两个数。
#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x){
T ch=getchar(),xx=1;x=0;
while(!isdigit(ch)) xx=ch=='-' ? -1 : xx,ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
x*=xx;
}
template<typename T>inline void print(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n;
__int128 x,y;
priority_queue<__int128> q,p;
int main(){
read(n);
for(int i=1;i<=n;++i){
read(x);
p.push(-x);
q.push(x);
}
for(int i=1;i<n;++i){
x=-p.top();p.pop();
y=-p.top();p.pop();
p.push(-x*y-1);
x=q.top();q.pop();
y=q.top();q.pop();
q.push(x*y+1);
}
print(-p.top()-q.top());
}