NOIP提高组模拟赛10


这套题咕咕咕咕咕了好久,还是决定先写一点吧

A. 第零题

\(i\)\(j\),在\(j\)处死了,那么与从\(j\)\(i\)死的次数是一样的,也就是说可以向上走到\(lca\)一下的最后一次死的点,最后这一段统计一下需不需要多死一次就好了,树上倍增记录\(i\)的第\(2^{j}\)级死亡点,1级死亡点\(dfs\)时候记录路径二分查找即可

code
#include
#include
#include
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>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]=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[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

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 
#include 
#include
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,然后正解呢,咕咕咕咕咕咕

相关