2024暑假集训测试6
前言
- 比赛链接。
挂分挂的最多的一集。
T1 不知道摩尔投票,被 2M 内存限制卡死。
T2 赛时打了个很像正解的莫队,赛时出题人发现了之后现往里加 hack,还一个捆绑里加一个,直接爆零了,我真的谢了,求求以后不要一个捆绑放一个 hack 了,给条活路吧。
T3 一眼看出线段树优化建图,但是不会打。
出题人说这次比赛就是为了让我们涨涨见识,学一些板子和套路。
T1 活动投票
摩尔投票,若新的
T2 序列
-
部分分
:暴力没什么好说的。 -
假做法:
对于每个
有其 表示其上一次出现的位置,对于每个 的 ,另 最为一组询问跑莫队,若该段询问区间里均存在只出现过一次的就合法,否则不合法。考虑做法的错误性,由于只考虑了
与 之间,进而没有考虑到一段子串中出现 次及以上的情况。提供一组
:1 2 1 2 1
。考虑优化成
的,由于莫队复杂度擦边且存在一定常数,仍会 ,没有继续深入研究,这个算法最多只能骗 了。 -
正解:
正解有很多种,我只改了一种,也是很套路的一种。
枚举右端点
, 表示区间 存在只出现一次的数的个数,考虑当 时如何转移。若
之前没有出现过,则 均 。否则定义
表示 上一次出现的位置, 均 , 均 。合法的条件就是任意时刻
均 。修改可以用线段树区间修改,查询可以线段树查询区间最小值。
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort #define f t[p] #define ls p<<1 #define rs p<<1|1 using namespace std; const int N=2e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int T,n,a[N],b[N],last[N],old[N],now[N],cnt[N]; struct aa {int l,r,val,add;}t[N<<2]; void build(int p,int l,int r) { f.l=l,f.r=r,f.val=f.add=0; if(l==r) return ; int mid=(l+r)>>1; build(ls,l,mid),build(rs,mid+1,r); f.val=min(t[ls].val,t[rs].val); } void spread(int p) { if(f.add==0) return ; t[ls].val+=f.add,t[ls].add+=f.add; t[rs].val+=f.add,t[rs].add+=f.add; f.add=0; } void change(int p,int l,int r,int d) { if(l>r) return ; if(l<=f.l&&r>=f.r) { f.val+=d,f.add+=d; return ; } spread(p); int mid=(f.l+f.r)>>1; if(l<=mid) change(ls,l,r,d); if(r>mid) change(rs,l,r,d); f.val=min(t[ls].val,t[rs].val); } int ask(int p,int l,int r) { if(l<=f.l&&r>=f.r) return f.val; spread(p); int mid=(f.l+f.r)>>1,ans=0x3f3f3f3f; if(l<=mid) ans=min(ans,ask(ls,l,r)); if(r>mid) ans=min(ans,ask(rs,l,r)); return ans; } signed main() { read(T); while(T--) { read(n); for(int i=1;i<=n;i++) read(a[i]), b[i]=a[i], cnt[i]=now[i]=last[i]=old[i]=0; sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+b[0],a[i])-b; for(int i=1;i<=n;i++) { last[i]=now[a[i]]; old[i]=last[last[i]]; now[a[i]]=i; } build(1,1,n); bool flag=0; for(int i=1;i<=n;i++) { if(cnt[a[i]]==0) { change(1,1,i,1); if(ask(1,1,i)<=0) { flag=1; puts("boring"); break; } } else { change(1,last[i]+1,i,1); change(1,old[i]+1,last[i],-1); if(ask(1,1,i)<=0) { flag=1; puts("boring"); break; } } cnt[a[i]]++; } if(!flag) puts("non-boring"); } }
T3 Legacy
-
上有同名原题,多倍经验 luogu P6348 [PA2011] Journeys。 -
部分分直接暴力建图就好了。
线段树优化建图板子。
-
建树:
建两棵树,一颗出树一颗入树。
对于入树,若该节点被连接则其子节点一定被连接,所以每个点向子节点连权值为
的边。对于出树,若该节点可向外连接则其父节点也一定能向外连接,故每个点向其父节点连权值为
的边。进了该点后就要出点,于是入树上每个节点向出树上对应节点连权值为
的边。 -
连边:
只考虑区间连区间这种更一般的情况,点就是长度为
的区间。对于区间
连 ,定义一个新的虚点 ,在出树上另 连 ,再另 连入树上 ,考虑权值不可重复计算,两次操作中仅让一个带权值,另一个权值为 。
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort #define f t[p] #define ls (p<<1) #define rs (p<<1|1) using namespace std; const int N=2e6+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int n,m,cnt,s,pos[N],dis[N]; bool vis[N]; int tot,head[N],to[N],nxt[N],w[N]; struct aa {int l,r;}t[N]; void add(int x,int y,int z) { nxt[++tot]=head[x]; to[tot]=y; w[tot]=z; head[x]=tot; } void build(int p,int l,int r) { f.l=l,f.r=r; add(p+4*n,p,0); if(l==r) {pos[l]=p; return ;} add(ls,p,0),add(rs,p,0); add(p+4*n,ls+4*n,0),add(p+4*n,rs+4*n,0); int mid=(l+r)>>1; build(ls,l,mid),build(rs,mid+1,r); } void change(int p,int x,int l,int r,int z,bool d) { if(l<=f.l&&r>=f.r) { if(d==0) add(p,x+8*n,0); else add(x+8*n,p+4*n,z); return ; } int mid=(f.l+f.r)>>1; if(l<=mid) change(ls,x,l,r,z,d); if(r>mid) change(rs,x,l,r,z,d); } void add(int a,int b,int c,int d,int z) { cnt++; change(1,cnt,a,b,z,0); change(1,cnt,c,d,z,1); } void dij(int s) { memset(dis,0x3f,sizeof(dis)); priority_queue<pair<int,int>>q; dis[s]=0; q.push(make_pair(0,s)); while(!q.empty()) { int x=q.top().second; q.pop(); if(vis[x]) continue; vis[x]=1; for(int i=head[x];i;i=nxt[i]) { int y=to[i],z=w[i]; if(dis[y]>dis[x]+z) { dis[y]=dis[x]+z; q.push(make_pair(-dis[y],y)); } } } } signed main() { read(n),read(m),read(s); build(1,1,n); for(int i=1,op,z,a,b,c,d;i<=m;i++) { read(op); if(op==1) read(a),read(c),read(z), b=a,d=c; if(op==2) read(a),read(c),read(d),read(z), b=a; if(op==3) read(c),read(a),read(b),read(z), d=c; add(a,b,c,d,z); } dij(pos[s]); for(int i=1;i<=n;i++) if(i==s) write(0),putchar(' '); else write(dis[pos[i]+n*4]==0x3f3f3f3f3f3f3f3f?-1:dis[pos[i]+n*4]),putchar(' '); }
T4 DP搬运工1
预设性
定义
对于新填一个数对于
-
与左右两边均不相邻,其后面填的数一定 ,故不产生贡献。 -
与左右两边中的一个相邻, 一定 之前填的数,其后面填的数一定 ,故贡献为 。 -
与左右两边均相邻, 一定 之前填的数,故贡献为 。
因此有转移:
-
若填在原序列两端:
-
若与原序列不相邻:
会产生一个新的段,同时不产生贡献,两端有两种可能,有
。 -
若与原序列相邻:
不会产生新的段,产生
的贡献,两端有两种可能,有 。
-
-
若填在原序列之间:
-
与原序列不存在相邻:
会产生一个新的段,对答案不产生贡献,
个段有 个空,有 。 -
与原序列一端相邻:
不会产生新的段,对答案产生
的贡献, 个段有 个空,与其任意一端相邻有两种情况,有 。 -
与原序列两端均相邻:
将两端接壤,故减少一个段,对答案产生
的贡献, 个段有 个空,有 。
-
最后答案
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=55,P=998244353; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int n,m,ans,f[N][N][N*N]; signed main() { read(n),read(m); f[1][0][0]=1; for(int i=2;i<=n;i++) for(int j=0;j<=n-i+1;j++) for(int k=0;k<=m;k++) { (f[i][j+1][k]+=f[i-1][j][k]*2)%=P; (f[i][j][k+i]+=f[i-1][j][k]*2)%=P; if(j!=0) (f[i][j+1][k]+=f[i-1][j][k]*j)%=P, (f[i][j][k+i]+=f[i-1][j][k]*j*2)%=P, (f[i][j-1][k+i*2]+=f[i-1][j][k]*j)%=P; } for(int i=0;i<=m;i++) (ans+=f[n][0][i])%=P; write(ans); }
总结
像 T1 这样怎么想都过不去的就不要抱有侥幸心理,而是去想新做法。
想到做法时先想想会不会被 hack。
多积累一些板子和套路,类似数颜色类型的区间特点统计的题考虑类似 T2 的套路。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!