[75] (NOIP集训) NOIP2024 加赛 8

A.flandre

我的做法是,所有数离散化之后扔进桶里,去枚举选择 [i,+) 内的数的贡献,在所有的 i 里取一个最大值作为答案

lbtl 指出可能存在最优答案是选择 [i+1,+) 内的所有数与值为 i 的部分数的数据

和 lbtl 交涉后尝试构造一组相同元素只选后一半的数据

ai=ai+1

  • 不选 i:设为 s
  • is+ai+k(ni)

构造一组 ai+k(ni)<0,ai+1+k(ni)k>0,即 ai<k(ni),ai+1k>k(ni),需要使 ai+kai+1,即 ai+1aik,不符合题设 ai=ai+1,无法构造出这种数据

说白了就是相等的数排完序后越靠前贡献越大,如果你选了后几个数,在这些数都是正贡献的情况下肯定要选后面的,都是负贡献的情况下肯定是为了选前面的数,否则不如不选

因此这种做法是有严格的正确性的,可能只是比较麻烦

赛时因为方案算对了答案算错了挂了 20 分,由于用方案代回去算一遍就行,因此没仔细去查错因

#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T&x){
    x=0;char ch=getchar();int f=1;
    while(!isdigit(ch)){
        if(ch=='-') f*=-1; 
        ch=getchar();
    }
    while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    x*=f;
}
#define int long long
int n,k;
int a[1000001];
int b[1000001];
int sum[1000001];
int sumcnt[1000001];
int sumnew[1000001];
int cnt[1000001];
vector<int>pos[1000001];
signed main(){
    read(n);read(k);
    for(int i=1;i<=n;++i){
        read(a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    int tmp=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;++i){
        a[i]=lower_bound(b+1,b+tmp+1,a[i])-b;
        cnt[a[i]]++;
        pos[a[i]].push_back(i);
    }
    for(int i=1;i<=tmp;++i){
        sumcnt[i]=sumcnt[i-1]+cnt[i];
        sum[i]=sum[i-1]+b[i]*cnt[i];
        sumnew[i]=sumnew[i-1]+cnt[i]*(sumcnt[i-1]);
    }
    int ans=-0x7fffffffffffffff,anscnt=1;
    for(int i=1;i<=tmp;++i){
        if(sum[tmp]-sum[i-1]+k*(sumnew[tmp]-sumnew[i]-(tmp-i)*sumcnt[i-1])>ans){
            ans=sum[tmp]-sum[i-1]+k*(sumnew[tmp]-sumnew[i]-(tmp-i)*sumcnt[i-1]);
            anscnt=i;
        }
    }
    if(ans<0){
        printf("0 0");
        return 0;
    }
    int tot=0,anss=0;
    for(int i=anscnt;i<=tmp;++i){
        for(int j:pos[i]){
            anss+=b[a[j]]+tot*k;
        }
        tot+=(int)pos[i].size();
    }
    printf("%lld %lld\n",anss,tot);
    for(int i=anscnt;i<=tmp;++i){
        for(int j:pos[i]) printf("%lld ",j);
    }
}

B.meirin

赛时偶遇 l=1nr=ln(i=lr(ai)×i=lr(bi)) 强如怪物拼尽全力无法战胜

正确的解法应该是拆贡献,考虑到你把上面这坨东西拆开一定是一个形如 i=1nj=1nci,jaibj (其中 ci,j 为系数) 的东西,因此考虑怎么求解这个 ci,j

一种比较直观的方法是将原式改成 l=1nr=ln(i=lr(ai×i=lr(bi))),这样改可以发现,只有 li,jr 的区间 [l,r] 会对 ci,j 有贡献,因此 ci,j 的实质就是同时包含 i,j 的区间个数

ci,j={i×(nj+1)ijj×(ni+1)otherwise

考虑求出这个玩意以后有什么用,如果你求出这东西,那么对 [l,r] 区间修改 bi 就相当于对答案增加了一个系数为 i=1nj=lraici,j 的贡献,可以直接维护前缀和 O(1) 处理

剩下的工作就是降这个预处理 ci,j 的复杂度了,可以考虑直接维护 pi=aij=1nci,j=j=1i1j(ni+1)aj+j=ini(nj+1)ai(因为 ai 不变),然后直接推一个从 pi1pi 的转移式,会发现里面有需要维护的两项 j=1i1j×ajj=in(nj+1)aj,直接套前缀和就行

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=1e9+7;
int n,q;
int a[500001],b[500001];
int x[500001],y[500002],s[500001];
int qz[500001],hz[500002];
int sums[500001];
signed main(){
    ios::sync_with_stdio(false);
    cin>>n>>q;
    for(int i=1;i<=n;++i){
        cin>>a[i];
    }
    for(int i=1;i<=n;++i){
        cin>>b[i];
    }
    for(int i=1;i<=n;++i){
        qz[i]=(qz[i-1]+i*a[i]+p)%p;
        hz[i]=(hz[i-1]+(n-i+1)*a[i]+p)%p;
    }  
    for(int i=1;i<=n;++i){
        x[i]=qz[i]%p;
        y[i]=((hz[n]-hz[i-1]+p)%p+p)%p;
    }
    int ans=0;
    for(int i=1;i<=n;++i){
        s[i]=(s[i-1]-x[i-1]+y[i]+p)%p;
        ans=(ans+b[i]*s[i])%p;
        sums[i]=(sums[i-1]+s[i])%p;
    }
    while(q--){
        int l,r,v;
        cin>>l>>r>>v;
        ans=((ans+(sums[r]-sums[l-1])*v%p)%p+p)%p;
        cout<<ans<<'\n';
    }
}

C.sakuya

发现期望是个诈骗东西,每两个节点相邻概率一样,题目实际上让求的是 i=1mj=1mdist(i,j)m

对上面这个东西不难想到,对标记节点重构一棵树,直接跑换根就能得到答案

然而这个题带修,因此需要一些智慧的处理

因为是单点修改,想到可以把每个边的贡献下放到两端节点处,这和上个题就一样了,设 ci,j(i,j) 这条边的贡献系数(即在答案中出现的次数),直接对每个点统计 fi=j,(i,j)Eci,j 直接就做完了,这题纯纯就是上个题的树上版本

这个 ci,j 求起来更简单了,考虑这是一棵树,一条边总会将树分成两部分,那么这条边对答案的贡献就是两个部分标记节点数量的乘积

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=998244353;
int n,m;
struct edge{
	int to,w;
};
vector<edge>e[500001];
bool vis[500001];
int viscnt[500001];
int f[500001];
int fa[500001];
inline int cal(int x,int y){ //find the ans of (x,y)
	if(x==fa[y]) return viscnt[y]*(m-viscnt[y]);
	else if(y==fa[x]) return viscnt[x]*(m-viscnt[x]);
	assert(0);
}
void dfs1(int now,int last){
	if(vis[now]) viscnt[now]=1;
	else viscnt[now]=0;
	fa[now]=last;
	for(edge i:e[now]){
		if(i.to!=last){
			dfs1(i.to,now);
			viscnt[now]+=viscnt[i.to];
		}
	}
}
int ans=0;
void dfs2(int now){
	for(edge i:e[now]){
		f[now]=(f[now]+cal(i.to,now))%p;
		if(i.to!=fa[now]){
			dfs2(i.to);
			ans=(ans+i.w*cal(i.to,now))%p;
		}
	}
}
int power(int a,int t){
	int base=a,ans=1;
	while(t){
		if(t&1){
			ans=ans*base%p;
		}
		base=base*base%p;
		t>>=1;
	}
	return ans;
}
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n-1;++i){
		int x,y,z;
		cin>>x>>y>>z;
		e[x].push_back({y,z});
		e[y].push_back({x,z});
	}
	int inv=power(m,p-2);
	for(int i=1;i<=m;++i){
		int x;cin>>x;
		vis[x]=true;
	}
	dfs1(1,0);
	dfs2(1);
	int q;cin>>q;
	while(q--){
		int x,k;
		cin>>x>>k;
		ans=(ans+f[x]*k%p+p)%p;
		cout<<ans*2*inv%p<<'\n';
	}
}
something

也需要迎来故事的终局了

希望一切都会好起来

posted @   HaneDaniko  阅读(46)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示