T1:Color the ball
本 题 是 差 分 的 模 板 题 本题是差分的模板题 本题是差分的模板题
差 分 一 下 两 两 之 间 的 差 值 , 区 间 加 影 响 的 差 值 就 是 c f [ l ] 和 差分一下两两之间的差值,区间加影响的差值就是cf[l]和 差分一下两两之间的差值,区间加影响的差值就是cf[l]和
c f [ r + 1 ] , 再 做 一 次 前 缀 和 就 做 完 了 cf[r+1],再做一次前缀和就做完了 cf[r+1],再做一次前缀和就做完了
#include
using namespace std;
const int N=1e5+5;
int n;
int a,b,p[N];
int main(){
scanf("%d",&n);
while(n){
memset(p,0,sizeof(p));
for(int i=1;i<=n;i++){
scanf("%d%d",&a,&b);
p[a]++,p[b+1]--;
}
int sum=0;
for(int i=1;i<=n;i++)
{
sum+=p[i];
printf("%d ",sum);
}
puts("");
scanf("%d",&n);
}
}
T2:数列游戏
线 段 树 是 可 以 的 线段树是可以的 线段树是可以的
因 为 修 改 和 询 问 是 分 开 的 , 所 以 我 们 可 以 先 差 分 , 计 算 出 每 个 数 , 再 前 缀 和 , 然 后 就 可 以 求 区 间 和 了 因为修改和询问是分开的,所以我们可以先差分,计算出每个数,再前缀和,然后就可以求区间和了 因为修改和询问是分开的,所以我们可以先差分,计算出每个数,再前缀和,然后就可以求区间和了
#include
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1e6+5;
int n,a,b;
ll cf[N],s[N],aa[N];
int main(){
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=a;i++){
int l,r;
ll c;
scanf("%d%d%lld",&l,&r,&c);
cf[l]=((c%mod+cf[l])%mod+mod)%mod;
cf[r+1]=((cf[r+1]-(c%mod))%mod+mod)%mod;
}
for(int i=1;i<=n;i++){
aa[i]=((aa[i-1]+cf[i])%mod+mod)%mod;
s[i]=((s[i-1]+aa[i])%mod+mod)%mod;
}
for(int i=1;i<=b;i++){
int l,r;
scanf("%d%d",&l,&r);
ll ans=((s[r]-s[l-1])%mod+mod)%mod;
printf("%lld\n",ans);
}
}
T3:松鼠的新家
对 于 路 径 上 的 值 相 邻 两 个 数 可 以 看 做 区 间 加 对于路径上的值相邻两个数可以看做区间加 对于路径上的值相邻两个数可以看做区间加
本 题 很 像 树 上 差 分 , 所 以 我 们 直 接 树 上 差 分 , 我 们 将 节 点 s 的 答 案 看 做 子 树 的 和 本题很像树上差分,所以我们直接树上差分,我们将节点s的答案看做子树的和 本题很像树上差分,所以我们直接树上差分,我们将节点s的答案看做子树的和
修 改 就 是 修改就是 修改就是
1. l c a 是 s 或 t , 直 接 a n s [ s ] 或 a n s [ t ] + + , a n s [ f a [ l c a ] ] − − 1.lca是s或t,直接ans[s]或ans[t]++,ans[fa[lca]]-- 1.lca是s或t,直接ans[s]或ans[t]++,ans[fa[lca]]−−
2. l c a 不 是 s 或 t , 将 a n s [ s ] + + , a n s [ t ] + + , a n s [ l c a ] − − , a n s [ f a [ l c a ] ] − − 2.lca不是s或t,将ans[s]++,ans[t]++,ans[lca]--,ans[fa[lca]]-- 2.lca不是s或t,将ans[s]++,ans[t]++,ans[lca]−−,ans[fa[lca]]−−
由 于 我 还 不 会 倍 增 求 l c a , 所 以 我 直 接 上 树 剖 了 由于我还不会倍增求lca,所以我直接上树剖了 由于我还不会倍增求lca,所以我直接上树剖了
注 意 点 : \color{blue}注意点: 注意点:
1. 因 为 是 路 径 , 并 不 能 将 相 邻 节 点 加 两 次 , 所 以 每 次 区 间 加 的 起 点 和 终 点 有 点 难 判 断 , 所 以 不 妨 直 接 加 1.因为是路径,并不能将相邻节点加两次,所以每次区间加的起点和终点有点难判断,所以不妨直接加 1.因为是路径,并不能将相邻节点加两次,所以每次区间加的起点和终点有点难判断,所以不妨直接加
s , t 的 , 并 且 保 证 不 加 s ( 如 题 意 , 第 一 个 点 不 用 放 ) , 最 后 一 个 节 点 要 减 去 ( 如 题 意 ) , 所 以 最 后 还 需 讨 论 一 下 , 因 为 情 况 比 较 多 s,t的,并且保证不加s(如题意,第一个点不用放),最后一个节点要减去(如题意),所以最后还需讨论一下,因为情况比较多 s,t的,并且保证不加s(如题意,第一个点不用放),最后一个节点要减去(如题意),所以最后还需讨论一下,因为情况比较多
2. 注 意 题 意 , 最 后 一 个 点 是 不 需 要 加 入 答 案 的 2.注意题意,最后一个点是不需要加入答案的 2.注意题意,最后一个点是不需要加入答案的
讨 论 的 情 况 , 讨论的情况, 讨论的情况,
#include
using namespace std;
const int N=3e5+5;
int n,a[N];
struct edge{
int link,v;
}q[N<<1];
int head[N],cnt=0;
void put(int u,int v){q[++cnt].v=v;q[cnt].link=head[u];head[u]=cnt;}
int dfn[N],size[N],son[N],fa[N],id[N],num[N],headline[N],depth[N],cf[N];
void dfs1(int s,int fath){
int maxson=0;
size[s]++;depth[s]=depth[fath]+1;
for(int i=head[s];i;i=q[i].link){
int v=q[i].v;
if(v==fath) continue;
fa[v]=s;
dfs1(v,s);
size[s]+=size[v];
if(size[v]>maxson){
maxson=size[v];
son[s]=v;
}
}
}
int tnt=0,cntid=0;
void dfs2(int s,int fath){
dfn[s]=++tnt;
if(son[s]){
id[son[s]]=id[s];headline[son[s]]=headline[s];
dfs2(son[s],s);
}
for(int i=head[s];i;i=q[i].link){
int v=q[i].v;
if(v==fath||v==son[s]) continue;
id[v]=++cntid;num[cntid]=v;headline[v]=v;
dfs2(v,s);
}
}
int ans[N];
int find(int u,int v){
while(headline[u]!=headline[v]){
if(depth[headline[u]]<depth[headline[v]]) swap(u,v);
u=fa[headline[u]];
}
if(headline[u]==headline[v]){
if(depth[u]<depth[v])
swap(u,v);
return v;
}
}
void solve(int now){
if(now>n) return;
while(now<=n){
if(now==2){
int u=a[now-1],v=a[now];
cf[v]++;
now++;
continue;
}
int u=a[now-1],v=a[now],uu=u,vv=v;;
if(headline[uu]==headline[vv]){
if(depth[uu]<depth[vv]) swap(uu,vv);
int lca=vv;
if(lca==u) {
cf[u]--,cf[v]++;
}
if(lca==v){
cf[fa[u]]++,cf[fa[v]]--;
}
now++;
continue;
}
int lca=find(u,v);
if(lca==0) lca=a[1];
cf[fa[u]]++,cf[v]++,cf[lca]--,cf[fa[lca]]--;
now++;
}
}
int sum=0;
void dfs(int s,int fath){
ans[s]=cf[s];
for(int i=head[s];i;i=q[i].link){
int v=q[i].v;
if(v==fath) continue;
dfs(v,s);
ans[s]+=ans[v];
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
put(u,v),put(v,u);
}
put(0,a[1]),put(a[1],0);
fa[a[1]]=0;
dfs1(a[1],0);
id[a[1]]=++cntid;num[cntid]=a[1];headline[a[1]]=a[1];
dfs2(a[1],0);
solve(2);
dfs(a[1],0);
ans[a[n]]--;
for(int i=1;i<=n;i++){
printf("%d\n",ans[i]);
}
}
T4:天天爱跑步
感谢这位大佬的博客让我明白了这道题
洛谷题面
不 知 道 这 题 和 差 分 有 什 么 关 系 , 但 既 然 放 到 这 个 专 题 中 , 就 当 做 是 吧 不知道这题和差分有什么关系,但既然放到这个专题中,就当做是吧 不知道这题和差分有什么关系,但既然放到这个专题中,就当做是吧
我 们 发 现 , 没 法 统 计 s 到 t 路 径 上 的 点 是 否 , 所 以 我 们 考 虑 点 S , 对 于 S , 我 们 考 虑 能 给 S 的 贡 献 我们发现,没法统计s到t路径上的点是否,所以我们考虑点S,对于S,我们考虑能给S的贡献 我们发现,没法统计s到t路径上的点是否,所以我们考虑点S,对于S,我们考虑能给S的贡献
这 样 , 我 们 根 据 N O I P 历 年 套 路 。 显 然 想 到 拆 分 路 径 , s — — l c a 和 l c a — — t 这样,我们根据NOIP历年套路。显然想到拆分路径,s——lca和lca——t 这样,我们根据NOIP历年套路。显然想到拆分路径,s——lca和lca——t
对 于 s — — l c a 的 路 径 , 我 们 发 现 能 贡 献 给 点 S 需 满 足 : d e e p [ s ] − d e e p [ S ] = w [ S ] 对于s——lca的路径,我们发现能贡献给点S需满足:deep[s]-deep[S]=w[S] 对于s——lca的路径,我们发现能贡献给点S需满足:deep[s]−deep[S]=w[S]
对 于 l c a — — t 的 路 径 , 我 们 发 现 能 贡 献 给 点 S 需 满 足 : d i s [ i ] − w [ S ] = d e e p [ v ] − d e e p [ S ] ( 其 中 , d i s [ i ] 为 s — — t 的 路 径 长 , 这 个 可 以 在 计 算 l c a 的 时 候 顺 便 记 下 来 ) 对于lca——t的路径,我们发现能贡献给点S需满足:dis[i]-w[S]=deep[v]-deep[S](其中,dis[i]为s——t的路径长,这个可以在计算lca的时候顺便记下来) 对于lca——t的路径,我们发现能贡献给点S需满足:dis[i]−w[S]=deep[v]−deep[S](其中,dis[i]为s——t的路径长,这个可以在计算lca的时候顺便记下来)
画 个 图 就 能 明 白 画个图就能明白 画个图就能明白
化 简 一 下 式 子 , 我 们 发 现 化简一下式子,我们发现 化简一下式子,我们发现
d e e p [ S ] + w [ S ] = d e e p [ S ] deep[S]+w[S]=deep[S] deep[S]+w[S]=deep[S]
w [ S ] − d e e p [ S ] = d i s [ i ] − d e e p [ v ] w[S]-deep[S]=dis[i]-deep[v] w[S]−deep[S]=dis[i]−deep[v]
很 快 想 到 用 树 状 数 组 , 但 本 题 应 该 用 桶 很快想到用树状数组,但本题应该用桶 很快想到用树状数组,但本题应该用桶
然后就做完了
注 意 点 : \color{green}注意点: 注意点:
#include
using namespace std;
const int N=3e5+5;
int n,m,head[N],cnt=0;
struct edge{
int link,v;
}q[N<<1];
int h1[N],h2[N*3],hcnt1=0,hcnt2=0;
void put(int x,int y){
q[++cnt].v=y;
q[cnt].link=head[x];
head[x]=cnt;
}
struct eg1{
int link,v;
}q1[N<<1];
struct eg2{
int link,v;
}q2[N<<1];
void put1(int x,int y){
q1[++hcnt1].v=y;
q1[hcnt1].link=h1[x];
h1[x]=hcnt1;
}
void put2(int x,int y){
q2[++hcnt2].v=y;
q2[hcnt2].link=h2[x];
h2[x]=hcnt2;
}
int d1[N*3],vals[N],d2[N*3],lg[N],w[N],fa[N][20],deep[N],s[N],t[N],lca[N],dis[N];
void dfs(int ss,int fath){
deep[ss]=deep[fath]+1;
for(int i=1;i<=lg[deep[ss]-1];i++){
fa[ss][i]=fa[fa[ss][i-1]][i-1];
}
for(int i=head[ss];i;i=q[i].link){
int v=q[i].v;
if(v==fath) continue;
fa[v][0]=ss;
dfs(v,ss);
}
}
int Lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
while(deep[x]>deep[y]){
x=fa[x][lg[deep[x]-deep[y]]];
}
if(x==y) return x;
for(int i=lg[deep[x]-1];i>=0;i--){//能跳的一定要跳完,所以下界是0,因为当两步到lca时,还需要判断0步的点是不是lca
if(fa[x][i]!=fa[y][i]){
x=fa[x][i],y=fa[y][i];
}
}
return fa[x][0];
}
int valsum[N];
void solve(int ss,int fath){
int tmp1=d1[deep[ss]+w[ss]],tmp2=d2[w[ss]-deep[ss]+n];
for(int i=head[ss];i;i=q[i].link)
{
int v=q[i].v;
if(v==fath) continue;
solve(v,ss);
}
d1[deep[ss]]+=vals[ss];
for(int i=h2[ss];i;i=q2[i].link){
int v=q2[i].v;
d2[dis[v]-deep[ss]+n]++;
}
valsum[ss]+=d1[deep[ss]+w[ss]]-tmp1+d2[w[ss]-deep[ss]+n]-tmp2;
for(int i=h1[ss];i;i=q1[i].link){
int v=q1[i].v;
int ls=s[i],rt=t[i];
d1[deep[ls]]--;//只出一个点,其它的点不用出,所以不是dp[deep[ls]]-=vals[ls]
d2[dis[v]-deep[rt]+n]--;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
put(u,v),put(v,u);
}
fa[1][0]=1;
lg[1]=0;
for(int i=2;i<=n;i++){
lg[i]=lg[i>>1]+1;
}
dfs(1,0);
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&s[i],&t[i]);
lca[i]=Lca(s[i],t[i]);
vals[s[i]]++;
dis[i]=deep[s[i]]+deep[t[i]]-2*deep[lca[i]];
put1(lca[i],i);
put2(t[i],i);
if(deep[lca[i]]+w[lca[i]]==deep[s[i]]) valsum[lca[i]]--;//如果lca有贡献,我们发现,lca既可以看成是s-lca的,也可以看成是lca-t的贡献,
//会重复计算,所以判断一下,提前减去
//直接看是否满足条件,看看有没有贡献
}
solve(1,0);
for(int i=1;i<=n;i++){
printf("%d ",valsum[i]);
}
}