9.23 pm

预期一测\Delta
a1001000
b1001000
c1001000
d1000-100
总分400300-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 数组表示前缀和,求 \max_{L\leq i\leq n}\left \{ sum_{i} -\min_{0\leq j \leq i-L}\left \{ sum_{j} \right \}\right \},随着 i 的增加,每次只有一个值

也就是 sum_{i-L}有可能更新 \min_{0\leq j \leq i-L}\left \{ sum_{j} \right \}

#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());
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值