【比赛记录】2025CSP-S模拟赛18
A | B | C | D | Sum | Rank |
---|---|---|---|---|---|
100 | 100 | 15 | 25 | 240 | 6/21 |
A. flandre
数据过弱放过一批错解。包括我的
正解:一个结论是选择的序列一定是原数组排序后的一段后缀。具体的证明是,如果 \(a_i\) 互不相同那么可以将区间一直往右移,如果相同那么一定可以不断插入进去,答案一定不劣。于是对每一段后缀都求出答案求最大值即可。
放个 UKE 造的 Hack 数据(也就是 Hack 掉我的那组)
Input:
11 4
-38 -37 1 2 3 4 5 6 7 8 9
Output:
190 11
1 2 3 4 5 6 7 8 9 10 11
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define lwrb lower_bound
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace asbt{
const int maxn=1e6+5;
int n,m,lsh[maxn],tot,tong[maxn];
pii a[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i].fir;
a[i].sec=i;
lsh[++tot]=a[i].fir;
}
sort(a+1,a+n+1);
sort(lsh+1,lsh+tot+1);
tot=unique(lsh+1,lsh+tot+1)-lsh-1;
for(int i=1;i<=n;i++){
a[i].fir=lwrb(lsh+1,lsh+tot+1,a[i].fir)-lsh;
}
int ans=0,pos=n+1;
for(int i=n,res=0;i;i--){
int t=a[i].fir;
res+=lsh[t];
res+=m*(n-i-tong[t]);
tong[t]++;
if(ans<res){
ans=res,pos=i;
}
}
cout<<ans<<" "<<n-pos+1<<"\n";
for(int i=pos;i<=n;i++){
cout<<a[i].sec<<" ";
}
return 0;
}
}
signed main(){return asbt::main();}
B. meirin
设 \(f_i\) 为 \(a\) 数组中所有包含 \(i\) 位置的区间的和,那么一个在 \(i\) 位置增加 \(k\) 对答案的贡献就是 \(f_i\times k\)。问题变为求 \(f\)。
考虑一个区间 \([l,r]\),设其区间和为 \(sum\),则会对所有 \(i\in[l,r]\) 的 \(f_i\) 产生 \(sum\) 的贡献。这提示我们做差分,也就是在 \(f_l\) 加 \(sum\),在 \(f_{r+1}\) 减 \(sum\),之后再做一遍前缀和即可。
考虑对于一个右端点 \(r\),其对应的 \(r\) 个左端点 \(l\in[1,r]\),\(f_l\) 要加上 \(sa_r-sa_{l-1}\),同时 \(f_{r+1}\) 要加上 \(sa_{l-1}-sa_r\)。其中 \(sa\) 是 \(a\) 的前缀和。设 \(i=l\),则有 \(f_i\) 要加上 \(sa_j- sa_{i-1}\),其中 \(j\in[i,n]\);设 \(i=r+1\),则 \(f_i\) 要加上 \(sa_j-sa_{i-1}\),其中 \(j\in[0,i-2]\)。于是:
时间复杂度线性,似乎需要卡常。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace IO{
const int bufsz=1<<20;
char ibuf[bufsz],*p1=ibuf,*p2=ibuf;
char obuf[bufsz],*p3=obuf,stk[50];
#define getchar() (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,bufsz,stdin),p1==p2)?EOF:*p1++)
#define flush() (fwrite(obuf,1,p3-obuf,stdout),p3=obuf)
#define putchar(ch) (p3==obuf+bufsz&&flush(),*p3++=(ch))
il int read(){
bool fu=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
fu^=ch=='-';
ch=getchar();
}
int x=0;
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return fu?-x:x;
}
il void write(int x){
int top=0;
do{
stk[++top]=x%10|48;
x/=10;
}while(x);
while(top){
putchar(stk[top--]);
}
putchar('\n');
}
struct FL{
~FL(){
flush();
}
}fl;
#undef getchar
#undef putchar
#undef flush
}
using IO::read;
using IO::write;
const int maxn=5e5+5,mod=1e9+7;
il int qum(int x){
return x<0?x+mod:x;
}
il int pls(int x,int y){
return x+y<mod?x+y:x+y-mod;
}
il int mns(int x,int y){
return x>=y?x-y:x-y+mod;
}
int n,m,a[maxn],f[maxn];
int main(){
// system("fc 2.out my.out");
// freopen("B.in","r",stdin);
// freopen("my.out","w",stdout);
n=read(),m=read();
int sum=0;
for(int i=1;i<=n;i++){
a[i]=pls(a[i-1],qum(read()));
sum=pls(sum,a[i]);
}
for(int i=1;i<=n;i++){
f[i]=pls(f[i-1],mns(sum,(n+1)*1ll*a[i-1]%mod));
}
int ans=0;
for(int i=1;i<=n;i++){
ans=pls(ans,f[i]*1ll*qum(read())%mod);
}
for(int i=1;i<=n;i++){
f[i]=pls(f[i-1],f[i]);
}
while(m--){
int l=read(),r=read();
ans=pls(ans,mns(f[r],f[l-1])*1ll*qum(read())%mod);
write(ans);
}
return 0;
}
// :-| <:-#
C. sakuya
设 \(a\) 序列中 \(i\) 和 \(j\) 相邻出现的次数为 \(\operatorname{cnt}(i,j)\),那么答案即为 \(\frac{\sum\operatorname{dis}(i,j)\times\operatorname{cnt}(i,j)}{m!}\)。显然 \(\operatorname{cnt}(i,j)=2\times(m-1)!\),于是问题变为求 \(\sum\operatorname{dis}\)。
然而这个是好求的,算出每一条边两端的关键点数即可算出它出现的次数 \(num\)。对于修改,求出每个点相邻的所有边的 \(num\) 之和就好了。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define pii pair<int,int>
using namespace std;
namespace asbt{
const int maxn=5e5+5,mod=998244353;
int n,m,sz[maxn],dep[maxn],dp[maxn],ans,q;
vector<pii> e[maxn];
il void dfs1(int u,int fa){
dep[u]=dep[fa]+1;
for(pii i:e[u]){
int v=i.fir;
if(v==fa){
continue;
}
dfs1(v,u);
sz[u]+=sz[v];
}
}
il int calc(int u,int v){
if(dep[u]<dep[v]){
swap(u,v);
}
return sz[u]*1ll*(m-sz[u])%mod;
}
il void dfs2(int u,int fa){
for(pii i:e[u]){
int v=i.fir,w=i.sec;
int t=calc(u,v);
(dp[u]+=t)%=mod;
if(v==fa){
continue;
}
(ans+=t*1ll*w%mod)%=mod;
dfs2(v,u);
}
}
il int qpow(int x,int y=mod-2){
int res=1;
while(y){
if(y&1){
res=res*1ll*x%mod;
}
x=x*1ll*x%mod,y>>=1;
}
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1,u,v,w;i<n;i++){
cin>>u>>v>>w;
e[u].pb(mp(v,w));
e[v].pb(mp(u,w));
}
for(int i=1,x;i<=m;i++){
cin>>x;
sz[x]++;
}
dfs1(1,0),dfs2(1,0);
int tms=qpow(m)*2%mod;
cin>>q;
while(q--){
int u,k;
cin>>u>>k;
(ans+=dp[u]*1ll*k%mod)%=mod;
cout<<ans*1ll*tms%mod<<"\n";
}
return 0;
}
}
int main(){return asbt::main();}
D. 红楼 ~ Eastern Dream
考虑根号分治。对于 \(x\le\sqrt{n}\) 的修改,我们可以记录 \(b_{i,j}\) 表示所有 \(p\equiv j\pmod{i}\) 的 \(p\) 的增量。于是在修改时只需要改变 \(b\) 数组即可。查询时枚举模数 \(i\),计算 \(l\) 和 \(r\) 所在的循环节,使用前缀和即可 \(O(1)\) 计算 \(i\) 的贡献。这一部分单次修改和查询时间复杂度都为 \(O(\sqrt{n})\)。
对于 \(x>\sqrt{n}\) 的修改,循环节只有 \(O(\sqrt{n})\) 个,我们可以分块维护增量的差分数组 \(d\),在修改时每段区间修改就都是 \(O(1)\) 的。查询时,我们要查的其实就是这个式子:
于是我们用分块维护 \(d_i\) 和 \(d_i\times i\) 即可。这部分复杂度也是 \(O(\sqrt{n})\)。
硬卡是能卡爆 long long
的,但是数据没卡 std 也没有,那就不管它了吧😉
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=2e5+5,B=450;
int n,m;
ll a[maxn],b[505][505],sb[505][505];
struct{
int bn,L[505],R[505],bel[maxn];
ll d[maxn],sd[505],di[maxn],sdi[505];
il void build(){
bn=(n+B-1)/B;
for(int i=1;i<=bn;i++){
L[i]=R[i-1]+1;
R[i]=min(R[i-1]+B,n);
for(int j=L[i];j<=R[i];j++){
bel[j]=i;
}
}
}
il void add(int p,ll x){
if(p>n){
return ;
}
d[p]+=x,sd[bel[p]]+=x;
di[p]+=x*p,sdi[bel[p]]+=x*p;
}
il ll queryd(int l,int r){
if(l>r){
return 0;
}
int pl=bel[l],pr=bel[r];
ll res=0;
if(pl==pr){
for(int i=l;i<=r;i++){
res+=d[i];
}
}
else{
for(int i=l;i<=R[pl];i++){
res+=d[i];
}
for(int i=L[pr];i<=r;i++){
res+=d[i];
}
for(int i=pl+1;i<pr;i++){
res+=sd[i];
}
}
return res;
}
il ll querydi(int l,int r){
if(l>r){
return 0;
}
int pl=bel[l],pr=bel[r];
ll res=0;
if(pl==pr){
for(int i=l;i<=r;i++){
res+=di[i];
}
}
else{
for(int i=l;i<=R[pl];i++){
res+=di[i];
}
for(int i=L[pr];i<=r;i++){
res+=di[i];
}
for(int i=pl+1;i<pr;i++){
res+=sdi[i];
}
}
return res;
}
}BL;
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
// cout<<cplx::usdmem();
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]+=a[i-1];
}
BL.build();
while(m--){
int opt;
cin>>opt;
if(opt==1){
int x,y;
ll k;
cin>>x>>y>>k;
y=min(y,x-1);
if(x<=B){
for(int i=0;i<=y;i++){
b[x][i]+=k;
}
sb[x][0]=b[x][0];
for(int i=1;i<x;i++){
sb[x][i]=sb[x][i-1]+b[x][i];
}
}
else{
for(int i=1;i<=n;i+=x){
BL.add(i,k),BL.add(i+y+1,-k);
}
}
}
else{
int l,r;
cin>>l>>r;
ll ans=a[r]-a[l-1];
for(int i=1;i<=B;i++){
int zl=(l-1)/i,zr=(r-1)/i;
int sl=(l-1)%i,sr=(r-1)%i;
if(zl==zr){
ans+=sb[i][sr]-(sl?sb[i][sl-1]:0);
}
else{
ans+=sb[i][i-1]-(sl?sb[i][sl-1]:0)+sb[i][sr];
ans+=sb[i][i-1]*(zr-zl-1);
}
}
ans+=(r-l+1)*BL.queryd(1,l-1);
ans+=(r+1)*BL.queryd(l,r);
ans-=BL.querydi(l,r);
cout<<ans<<"\n";
}
}
return 0;
}
}
int main(){return asbt::main();}