NOIP2024 前集训:NOIP2024加赛 8
前言
music
《铃芽之旅》
du du du du du du du du du du du du du du du du
du du du du du du du du du du du du du du du du
du du du du du du du du du du du du du du du du
du du du du du du du du du du du du du du du du
于 你的生命之中
红与蓝的线缠绕交错
在 心间划上最后一笔
勾勒出你的脉络
那 随风飘荡的声音 从不曾被吹散片刻
是 藏在心中的话 想对你说却还没能说
人生啊 短暂如花
微风啊 拂过脸颊
星光啊 照谁的家
我们是 宇宙一刹
别再问 别再问 这样的我 为什么哭着
眼泪替我诉说
意义是你 意义是我
意义总是 难以追寻的
是我们相遇过 谁奋力呐喊着
却吼不破命运的壳
只能是你 只能由你
触碰双手轻轻唤醒我
沉睡千年的心搏
意义是你 意义是我
意义是否 最终跨越呢
相隔时空的你我
是愚昧又如何 是丑恶又如何 是望着前方正确的因果
是想与你携手走过
如今我已想不起
曾经珍惜的回忆
却似有万千言语
深深埋葬在我心底
或许就这样吧 或许就这样了
还有什么遗憾的 我已交付所有的自己
或许就这样吧 或许就这样了
多么想让你听见
这颗破碎又顽强的心
只想靠近你
别再问 别再问 这样的我 为什么哭着
眼泪替我诉说
意义是你 意义是我 意义总是 难以追寻的
是我们相遇过 谁奋力呐喊着
却吼不破命运的壳
只能是你只能由你
触碰双手轻轻唤醒我
沉睡千年的心搏
意义是你 意义是我
意义是否 最终跨越呢
相隔时空的你我
是愚昧又如何 是丑恶又如何
是望着前方 正确的因果
是想与你携手走过
这首歌我去年暑假集训曾在机房非常大声唱过,当时我还在普及组,和现在机房里的绝大多数人不在用同一个机房,当时什么活动都不是就是一边打代码一边唱歌,也没人管我,小飞谷进来都没管,现在再像这样肯定不行了。
今天中午打电话我妈告诉我遇到诈骗了,诈骗的不知道从哪儿弄来我的短信给我妈发消息交钱,不过我这辈子没用过短信,再说我有事儿肯定打电话,最主要的是我从来没朝我妈要过钱,所以我妈一眼看出来是诈骗了。
但是挺离谱的诈骗的为啥会搞到我手机号还能知道我妈是谁还能知道我是学 OI 的?
T1 赛时不知道贪心是对的,奔着随机数据去的结果 A 了;T2 忘初始化保龄了;T4 没开 long long(开了,但是不知道啥时候给删了)挂了 35。
明天不想打了呜呜呜,还没打板子呢,但是如果明天不打今天可能就是最后一场了。
T1 flandre
贪心的发现一定是优先选大的,然后肯定是把小的排在前面。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e6+10,mx=1e6;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char a=getchar_unlocked();
for(;!isdigit(a);a=getchar_unlocked()) if(a=='-') z=0;
for(;isdigit(a);a=getchar_unlocked()) x=(x<<1)+(x<<3)+(a^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,tot,top,a[N],pos[N],sta[N]; ll k,ans,sum; vector<int>e[N<<1];
signed main()
{
freopen("flandre.in","r",stdin),freopen("flandre.out","w",stdout);
read(n,k);
for(int i=1;i<=n;i++) read(a[i]),e[a[i]+mx].push_back(i);
for(int i=mx;~i;i--) if(!e[i+mx].empty())
{
ans+=1ll*e[i+mx].size()*(i+k*m),m+=e[i+mx].size();
for(int j:e[i+mx]) pos[++tot]=j;
}
sum=ans; for(int i=-1,tmp=0;i>=-mx;i--,tmp=0) if(!e[i+mx].empty())
{
for(int j=1;j<=e[i+mx].size();j++) if(sum+j*(k*m+i)>ans)
{
for(int h=1;h<=top;h++) pos[++tot]=sta[h];
for(int h=tmp;h<j;h++) pos[++tot]=e[i+mx][h];
ans=sum+j*(k*m+i),tmp=j,top=0;
}
for(int j=tmp;j<e[i+mx].size();j++) sta[++top]=e[i+mx][j];
sum+=e[i+mx].size()*(i+k*m),m+=e[i+mx].size();
}
write(ans,tot),puts(""),reverse(pos+1,pos+1+tot);
for(int i=1;i<=tot;i++) write(pos[i]),putchar_unlocked(' ');
}
T2 meirin
往数据结构上想了,实际上是道数学题。
考虑把式子拆开,这是简单的,有很多种方式可以把他拆成单词 \(O(n)\) 的,但是需要把他拆成修改 \(O(1)\sim O(\log)\) 的。
发现 \(a_i\) 始终不变,考虑把 \(a_i\) 当成系数拎出来,转换后有:
\(sum_i\) 表示前缀和,发现转换为了 \(b_i\) 系数的形式,于是就可以直接算贡献了。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=5e5+10,P=1e9+7;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char a=getchar_unlocked();
for(;!isdigit(a);a=getchar_unlocked()) if(a=='-') z=0;
for(;isdigit(a);a=getchar_unlocked()) x=(x<<1)+(x<<3)+(a^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,ans,a[N],b[N];
inline int mod(int x,int y) {return (x+=y)>=P?x-P:x;}
signed main()
{
freopen("meirin.in","r",stdin),freopen("meirin.out","w",stdout);
read(n,m);
for(int i=1,x;i<=n;i++) read(x),a[i]=mod(a[i-1],mod(x,P));
for(int i=1;i<=n;i++) a[i]=mod(a[i-1],a[i]);
for(int i=1,x;i<=n;i++)
{
b[i]=mod(1ll*mod(a[n],P-a[i-1])*i%P,P-1ll*a[i-1]*(n-i+1)%P);
read(x),ans=mod(ans,1ll*b[i]*mod(x,P)%P),b[i]=mod(b[i-1],b[i]);
}
for(int l,r,x;m;m--)
{
read(l,r,x);
write(ans=mod(ans,1ll*mod(b[r],P-b[l-1])*mod(x,P)%P)),puts("");
}
}
T3 sakuya
考虑统计边 \((x,y)\) 出现的次数,断开这条边树会分成两部分,这条边被计算当且仅当相邻的两点分别在两部分中,那么这部分可以用树形 DP 做了。
考虑两点相邻的概率,用捆绑法求出两点相邻的概率为 \(\dfrac{2(m-1)!}{m!}=\dfrac{2}{m}\)。
加贡献的时候直接根据求出的 DP 加就可以了。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=5e5+10,P=998244353;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char a=getchar_unlocked();
for(;!isdigit(a);a=getchar_unlocked()) if(a=='-') z=0;
for(;isdigit(a);a=getchar_unlocked()) x=(x<<1)+(x<<3)+(a^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,tot,ans,f[N],sz[N]; ll inv; int head[N],nxt[N<<1],to[N<<1],w[N<<1];
inline int mod(int x,int y) {return (x+=y)>=P?x-P:x;}
inline int qpow(ll a,int b)
{ll res=1; for(;b;(a*=a)%=P,b>>=1) (b&1)&&((res*=a)%=P); return res;}
inline void add(int x,int y,int z)
{nxt[++tot]=head[x],to[tot]=y,w[tot]=z,head[x]=tot;}
inline void dfs1(int x,int t)
{for(int i=head[x],y;y=to[i];i=nxt[i]) if(y!=t) dfs1(y,x),sz[x]+=sz[y];}
inline void dfs2(int x,int t)
{
f[t]=mod(f[t],1ll*sz[x]*(m-sz[x])%P);
for(int i=head[x],y,tmp;y=to[i];i=nxt[i]) if(y!=t)
{
f[y]=mod(f[y],tmp=1ll*sz[y]*(m-sz[y])%P);
ans=mod(ans,1ll*tmp*w[i]%P),dfs2(y,x);
}
}
signed main()
{
freopen("sakuya.in","r",stdin),freopen("sakuya.out","w",stdout);
read(n,m),inv=qpow(m,P-2)*2%P;
for(int i=1,x,y,z;i<n;i++) read(x,y,z),add(x,y,z),add(y,x,z);
for(int i=1,x;i<=m;i++) read(x),sz[x]=1; dfs1(1,0),dfs2(1,0),read(m);
for(int x,k;m;m--,write(ans*inv%P),puts(""))
read(x,k),ans=mod(ans,1ll*f[x]*k%P);
}
T4 红楼 ~ Eastern Dream
已经把根号分治写脸上了,问题在于怎么分别处理。
对于 \(x\le \sqrt n\) 的因为这种点最多只有 \(\sqrt n\) 个,所以直接算即可。
对于 \(x>\sqrt n\) 的,直接线段树会多个 \(\log\) 过不去,考虑序列分块加差分,从而做到每个块 \(O(1)\) 更新 \(O(\sqrt n)\) 查询,维护差分的方式和树状数组维护区间改区间查相同。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10,M=300,L=1000;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char a=getchar_unlocked();
for(;!isdigit(a);a=getchar_unlocked()) if(a=='-') z=0;
for(;isdigit(a);a=getchar_unlocked()) x=(x<<1)+(x<<3)+(a^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,len,vl[N],vr[N],pos[N]; ll ans,a[N],b[N],c[N],s1[L],s2[L],d[M][M];
inline void add(int l,int r,int k)
{
c[l]+=k,s1[pos[l]]+=k,s2[pos[l]]+=1ll*(vr[pos[l]]-l+1)*k;
c[++r]-=k,s1[pos[r]]-=k,s2[pos[r]]-=1ll*(vr[pos[r]]-r+1)*k;
}
inline ll ask1(int x,int y) {return ~y?(y/x)*b[x]+d[x][y%x]:0;}
inline ll ask2(int l,int r)
{
ll res=0; int x=pos[l],y=pos[r];
for(int i=1;i<x;i++) res+=s1[i]*(r-l+1);
for(int i=vl[x];i<l;i++) res+=c[i]*(r-l+1);
if(x==y) {for(int i=l;i<=r;i++) res+=c[i]*(r-i+1); return res;}
for(int i=l;i<=vr[x];i++) res+=c[i]*(r-i+1);
for(int i=x+1;i<y;i++) res+=s1[i]*(r-vr[i])+s2[i];
for(int i=vl[y];i<=r;i++) res+=c[i]*(r-i+1); return res;
}
signed main()
{
freopen("scarlet.in","r",stdin),freopen("scarlet.out","w",stdout);
read(n,m),len=max(1,(int)sqrt(n)/2);
for(int i=1;i<=n;i++) read(a[i]),a[i]+=a[i-1],pos[i]=(i-1)/len+1;
for(int i=1;i<=pos[n];i++) vl[i]=vr[i-1]+1,vr[i]=min(n,i*len);
for(int op,x,y,k;m;m--)
{
read(op,x,y); if(op&1)
{
read(k),y=min(x-1,y); if(x<=len)
{
b[x]+=1ll*k*(y+1);
for(int i=0;i<=y;i++) d[x][i]+=1ll*(i+1)*k;
for(int i=y+1;i<x;i++) d[x][i]+=1ll*(y+1)*k;
}
else for(int i=1;i<=n;i+=x) add(i,min(n,i+y),k); continue;
}
for(int i=1;i<=len;i++) ans+=ask1(i,y-1)-ask1(i,x-2);
write(ans+a[y]-a[x-1]+ask2(x,y)),puts(""),ans=0;
}
}