# NK 0827 练习赛 反思

NK 0827 练习赛 反思

小小总结
期望得分 200 实际得分 210 还算可以的啦 但还是要继续加油 要稳住不要翻车的啊


A 异或

题面描述
何老板给你一个长度为(N)的整数数列 。
请你帮他找出满足下列条件的数字对([l,r]) 的个数:
(a_l)$a_{l+1}$(a_{l+2})...(a_r)=(a_l)+(a_{l+1})+(a_{l+2})...(a_r)
"(xor)"表示“异或”,对应c++中的符号是"^"
输入格式
第一行,一个整数
第二行, 个空格间隔的整数
输出格式
一个整数,表示满足条件的数字对的个数

解:

  1. 双指针的裸题
    首先异或被称为"不进位的加法"
    那么区间就满足单调性
    所以我们可以用双指针 每次右指针扩展 然后左边指针移动到合法位置 中间的都行
    code:
//
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
ll sum;
ll n;
#define maxnn 400000
ll a[maxnn];
ll tot=0;
int main() {
//	freopen("axor.in","r",stdin);
	//freopen("axor.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1; i<=n; i++) {
		scanf("%lld",&a[i]);
	}
	sum=a[1];
	int now=1;
	for(int i=2; i<=n; i++) {
		if((sum^a[i])==sum+a[i]) tot+=i-now+1,sum+=a[i];
		else {
			while((sum^a[i])!=sum+a[i]) {
				sum^=a[now];
				now++;
			}
			sum+=a[i],tot+=i-now+1;
		}
	}
	printf("%lld",tot+1);
}
 2.二分+倍增的裸题

我们注意到区间具有单调性 并且数据比较大 所以可以考虑二分+倍增 确定区间的起点 然后跳到合法的最远的区间

code 已死


B 管辖

问题描述
何老板给你一棵树,树中共N个节点,编号1到N,1号点为根。
每个节点都有一个权值,其中第i个节点的权值为 (a_i)
树中共有(N-1)条边,每条边都有一定的长度,第i条边长度为 (w_i)
对于树中任意一点i,从i出发往根的路径中,距离i不超过(a_i)的点,都可以管辖i号点。
何老板想知道,每个点管辖的点有多少个(不包括自己)。他拜托你帮忙计算一下。

解:

  1. 倍增+树上差分的裸题
    不过好像很多据老 都写挂了.....
    code:
//
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
#define  iuiu 200100
#define maxn  200100
#define ll long long
ll dis[iuiu][20];
int f[iuiu][20];
int n;
int w[maxn];
int las[maxn],en[maxn],tot,nex[maxn];
int  cnt[maxn],le[maxn];

void add(int a,int b,ll c) {
	en[++tot]=b;
	nex[tot]=las[a];
	las[a]=tot;
	le[tot]=c;
}
int go_up(int s,int v) {
	int u=ceil(log2(n));
	for(int k=u; k>=0; k--) {
		if(f[v][k]&&(s>=dis[v][k])) {
			s-=dis[v][k];
			v=f[v][k];
		}
	}
	return v;
}
void dfs(int v,int fa,int l) {
	f[v][0]=fa;
	dis[v][0]=l;
	int u=ceil(log2(n));
	for(int i=1; i<=u; i++) {
		f[v][i]=f[f[v][i-1]][i-1];
		dis[v][i]=dis[v][i-1]+dis[f[v][i-1]][i-1];
	}
	for(int i=las[v]; i; i=nex[i]) {
		int t=en[i];
		if(t!=fa) {
			dfs(t,v,le[i]);
			cnt[v]+=cnt[t];
		}
	}
	return ;
}
int main() {
	//freopen("brun.in","r",stdin);
	//freopen("brun.out","w",stdout);
	scanf("%d",&n);
	int x,y;
	for(int i=1; i<=n; i++) {
		scanf("%d",&w[i]);
	}
	for(int i=2; i<=n; i++) {
		scanf("%d%d",&x,&y);
		add(x,i,y);
	}
	dfs(1,0,0);
	for(int i=1; i<=n; i++) {
		int r=go_up(w[i],i);
		{
			cnt[f[i][0]]++;
			cnt[f[r][0]]--;
		}
	}
	dfs(1,0,0);
	for(int i=1; i<=n; i++) {
		printf("%d ",cnt[i]);
	}
}

2.搜索回溯+二分查找
注意到这是一条链 对于每一个深度 都是一个被争抢的资源
所以我们可以从根节点开始搜索 然后二分查找
code:

//  
#include<stdio.h>  
#include<bits/stdc++.h>  
using namespace std;  
#define  iuiu 400100  
#define maxn  400100  
#define ll long long  
int n;  
ll f[maxn]; 
ll w[maxn];  
ll las[maxn],en[maxn],tot,nex[maxn];  
ll  cnt[maxn],le[maxn];  
ll ty[maxn],tx[maxn];
ll o=0;
void add(int a,int b,ll c) {  
    en[++tot]=b;  
    nex[tot]=las[a];  
    las[a]=tot;  
    le[tot]=c;  
}  
void dfs(int v,int fa,ll l) {  
    		f[v]=fa;
			ty[++o]=l; 
            tx[o]=v;  
			if(o)
            {  
                cnt[f[v]]++;  
                cnt[f[tx[lower_bound(ty+1,ty+1+o,l-w[v])-ty]]]--; 
            }   
			
    for(int i=las[v]; i; i=nex[i]) {  
        int t=en[i];  
        if(t!=fa) {  
            dfs(t,v,l+le[i]);  
        }  
    }   	
    		o--;
    return ;  
}  
void dfsD(int v,int fa) {  

    for(int i=las[v]; i; i=nex[i]) {  
        int t=en[i];  
        if(t!=fa) {  
            dfsD(t,v);  
            cnt[v]+=cnt[t]; 
        }  
    }  
    return ;  
}  
int main() {  
    //freopen("brun.txt","r",stdin);  
    //freopen("nnn.out","w",stdout);  
    scanf("%d",&n);  
    ll x,y;  
    for(int i=1; i<=n; i++) {  
        scanf("%lld",&w[i]);  
    }  
    for(int i=2; i<=n; i++) {  
        scanf("%lld%lld",&x,&y);  
        add(x,i,y);
		add(i,x,y); 
    }  
    dfs(1,0,0); 
    dfsD(1,0);  
    for(int i=1; i<=n; i++) {  
        printf("%lld ",cnt[i]);  
    }  
} 

C 打分
问题描述
何老板给你一棵树,树中共(N)个节点,编号(1)(N)(1)号点为根。 每个节点都有一个标记,要么为(0),要么为(1)
同时每个节点都有一个得分:
若i号节点的标记为(0),则(i)的得分等于它的所有儿子节点中,得分最低的那一个的分值。
若i号节点的标记为(1),则(i)的得分等于它的所有儿子节点中,得分最高的那一个的分值。
一开始叶子节点都没有分值,何老板需要给叶子打分。若这棵树总共有(M)个叶子节点,那么打分的分值是([1,M])区间中的整数,每个数字只能使用一次。
何老板想知道,怎样打分,才能使得根节点的得分尽可能大。请你计算出根节点可能的最大分值。

(Solution:)
排名问题的模板 但我还是太ruo 现在我来复习一下
打分的区间是([1.M]) 有没有想到什么啊
wo 开始想的是贪心 后来发现不行 就设了一个估计函数 混了10pts ...

注意到 我们应该换个思路 不只是 纠结于 贪心地 首先给每个点打分 那样你就掉坑里了
而应该关注子树的排名情况
对于(i)号节点 定义 (size[i]) 是它子树内的叶子节点的个数 (s)(i)号点的儿子
(f[i])表示 i 能够得到的最佳排名
那么状态转移方程就很好写了

  • 对于(i)号节点值为(1) 那么也就代表它能够取儿子排名内的最大 但是儿子取得的最佳排名不一定是最后的
    这取决于儿子能够获取的最佳排名在它子树内的情况
    也就是说 儿子的排名后面的都已经被填死了 所以 i 最优能够得到的 排名为 (f[i]-(size[s]-f[s]))

  • 对于(i)号节点值为(0) 那么也就代表它只能够取儿子排名内的最小的 但是儿子取得的最佳排名不一定是最靠前的 但(f[i])的值要尽可能大
    画个图可以发现 因为最优排名之前的已经被填充了 所以要让最小的最大 答案就是 被填充的总数+1 用数学式子表达为
    (sum_s (f[s]-1)) +1

summary: 要加油鸭 不要骄傲呢 信竞的道路长且难 但是你要稳住 充满活力啊

刀剑映出了战士的心。而我的心,漆黑且残破
原文地址:https://www.cnblogs.com/OIEREDSION/p/11427900.html