NOIP提高组模拟赛10
这套题咕咕咕咕咕了好久,还是决定先写一点吧
A. 第零题
从\(i\)到\(j\),在\(j\)处死了,那么与从\(j\)到\(i\)死的次数是一样的,也就是说可以向上走到\(lca\)一下的最后一次死的点,最后这一段统计一下需不需要多死一次就好了,树上倍增记录\(i\)的第\(2^{j}\)级死亡点,1级死亡点\(dfs\)时候记录路径二分查找即可
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200005;
struct edge{
int to,val,net;
}e[maxn<<1|1];
int head[maxn],tot,n,k;
void add(int u,int v,int w){
e[++tot].net=head[u];
head[u]=tot;
e[tot].to=v;
e[tot].val=w;
}
int fa[maxn][21],dep[maxn],jd[maxn][21],top,rem[maxn];
long long cos[maxn],sum[maxn];
int ef(int x){
int l=1,r=x;
while(l<r){
int mid=(l+r)>>1;
if(sum[x]-sum[mid]>=k)l=mid+1;else r=mid;
}
return rem[l-1];
}
void dfs(int x,int fx){
rem[++top]=x;sum[top]=sum[top-1]+cos[x];
jd[x][0]=ef(top);fa[x][0]=fx;dep[x]=dep[fx]+1;
for(int i=head[x];i;i=e[i].net){
int v=e[i].to;if(v==fx)continue;
cos[v]=e[i].val;dfs(v,x);
}
--top;
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;--i)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=20;i>=0;--i)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int ask(int s,int t){
int lca=LCA(s,t);
int ans=0;
for(int i=20;i>=0;--i){
if(dep[lca]<dep[jd[s][i]]){
ans+=(1<<i);
s=jd[s][i];
}
if(dep[lca]<dep[jd[t][i]]){
ans+=(1<<i);
t=jd[t][i];
}
}
if(dep[jd[s][0]]>=dep[lca])++ans,s=jd[s][0];
if(dep[jd[t][0]]>=dep[lca])++ans,t=jd[t][0];
int sum=0;
while(s!=lca)sum+=cos[s],s=fa[s][0];
while(t!=lca)sum+=cos[t],t=fa[t][0];
if(sum>=k)++ans;
return ans;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<n;++i){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
//rem[0]=1;
dfs(1,1);
for(int i=1;i<=20;++i)for(int x=1;x<=n;++x)fa[x][i]=fa[fa[x][i-1]][i-1],jd[x][i]=jd[jd[x][i-1]][i-1];
int q;scanf("%d",&q);
for(int i=1;i<=q;++i){
int s,t;scanf("%d%d",&s,&t);
printf("%d\n",ask(s,t));
}
return 0;
}
B. 第负一题
cao年代久远,忘了,还得重新看题解。。。。。。
分治+DP
每次计算跨过中点的区间的贡献
\(ll[i][0/1]\)向左,选/不选\(mid\)的最大值
\(rr[i][0/1]\)向右,选/不选\(mid\)的最大值
对答案的贡献为
\(\displaystyle\sum^{mid}_{i=l}\sum^{r}_{j=mid+1}max(ll[i][0]+rr[j][1],ll[i][1]+rr[j][0],ll[i][0]+rr[j][0])\)
并不好求,转化一下
\(max(ll[i][0]+rr[j][1],ll[i][1]+rr[j][0],ll[i][0]+rr[j][0])=max(ll[i][1]-ll[i][0],rr[j][1]-rr[j][0],0)+ll[i][0]+rr[j][0]\)
设
\(fl[i]=max(ll[i][1]-ll[i][0],0)\)
\(fr[i]=max(rr[i][1]-rr[i][0],0)\)
对答案贡献转化为
\(\displaystyle\sum^{mid}_{i=l}\sum^{r}_{j=mid+1}(ll[i][0]+rr[j][0]+max(fl[i],fr[j]))\)
对\(fl[]\) \(fr[]\)排序,单调指针扫一遍就行了
code
#include <cstring>
#include <cstdio>
#include<algorithm>
using namespace std;
const long long maxn=200005;
const long long mod=998244353;
long long n,a[maxn];
const long long inf=0x3f3f3f3f3f3f3f;
long long fl[maxn],fr[maxn],ll[maxn][2],rr[maxn][2];
long long dp[maxn][2],ans;
void work(int l,int r){
if(l==r){
ans=(ans+a[l]);
return;
}
int mid=(l+r)>>1;work(l,mid);work(mid+1,r);
//向左,不选mid
dp[mid][0]=0;dp[mid][1]=-inf;
for(int i=mid-1;i>=l;--i)dp[i][0]=max(dp[i+1][0],dp[i+1][1]),dp[i][1]=dp[i+1][0]+a[i],ll[i][0]=max(dp[i][0],dp[i][1]);
//向左,选mid
dp[mid][0]=-inf;dp[mid][1]=a[mid];
for(int i=mid-1;i>=l;--i)dp[i][0]=max(dp[i+1][0],dp[i+1][1]),dp[i][1]=dp[i+1][0]+a[i],ll[i][1]=max(dp[i][0],dp[i][1]);
//向右,不选mid+1
dp[mid+1][0]=0;dp[mid+1][1]=-inf;
for(int i=mid+2;i<=r;++i)dp[i][0]=max(dp[i-1][0],dp[i-1][1]),dp[i][1]=dp[i-1][0]+a[i],rr[i][0]=max(dp[i][0],dp[i][1]);
//向右,选mid+1
dp[mid+1][0]=-inf;dp[mid+1][1]=a[mid+1];
for(int i=mid+2;i<=r;++i)dp[i][0]=max(dp[i-1][0],dp[i-1][1]),dp[i][1]=dp[i-1][0]+a[i],rr[i][1]=max(dp[i][0],dp[i][1]);
ll[mid][0]=0;ll[mid][1]=a[mid];
rr[mid+1][0]=0;rr[mid+1][1]=a[mid+1];
for(int i=l;i<=mid;++i)fl[i]=max(ll[i][1]-ll[i][0],0ll),ans=(ans+ll[i][0]*(r-mid))%mod;
for(int i=mid+1;i<=r;++i)fr[i]=max(rr[i][1]-rr[i][0],0ll),ans=(ans+rr[i][0]*(mid-l+1))%mod;
sort(fl+l,fl+mid+1);sort(fr+mid+1,fr+r+1);
int pl=l,pr=mid+1;
while(pl<=mid){
while(fr[pr]<=fl[pl]&&pr<=r)ans=(ans+fr[pr]*(pl-l))%mod,++pr;
ans=(ans+fl[pl]*(pr-mid-1))%mod;++pl;
}
while(pr<=r)ans=(ans+fr[pr]*(mid-l+1))%mod,++pr;
return;
}
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
work(1,n);
printf("%lld\n",ans);
return 0;
}
C. 第负二题
这就是我咕咕咕咕咕了这篇题解这么长时间的原因
这道题啊,暴力水了90pts,然后正解呢,咕咕咕咕咕咕