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++中的符号是"^"
输入格式
第一行,一个整数
第二行, 个空格间隔的整数
输出格式
一个整数,表示满足条件的数字对的个数
解:
- 双指针的裸题
首先异或被称为"不进位的加法"
那么区间就满足单调性
所以我们可以用双指针 每次右指针扩展 然后左边指针移动到合法位置 中间的都行
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号点。
何老板想知道,每个点管辖的点有多少个(不包括自己)。他拜托你帮忙计算一下。
解:
- 倍增+树上差分的裸题
不过好像很多据老 都写挂了.....
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: 要加油鸭 不要骄傲呢 信竞的道路长且难 但是你要稳住 充满活力啊