暑假集训CSP提高模拟16
1.高一上六月下旬日记2.【MX-S1】梦熊周赛 · 提高组 1(同步赛)3.牛客周赛 Round 494.高一上七月上旬日记5.Denso Create Programming Contest 2024(AtCoder Beginner Contest 361)6.NOIP2024模拟17.NOIP2024模拟28.高一上七月中旬日记9.NOIP2024模拟310.CSP提高组模拟111.暑假集训CSP提高模拟112.暑假集训CSP提高模拟213.暑假集训CSP提高模拟314.暑假集训CSP提高模拟415.高一上七月下旬日记16.暑假集训 加赛117.暑假集训CSP提高模拟518.暑假集训CSP提高模拟619.加赛220.暑假集训CSP提高模拟721.暑假集训CSP提高模拟822.暑假集训CSP提高模拟923.暑假集训CSP提高模拟1024.暑假集训CSP提高模拟1125.暑假集训CSP提高模拟1226.高一上八月上旬日记27.暑假集训CSP提高模拟1328.欢欢乐乐赛赛29.暑假集训CSP提高模拟1430.暑假集训CSP提高模拟15
31.暑假集训CSP提高模拟16
32.暑假集训CSP提高模拟1733.高一上八月中旬日记34.暑假集训CSP提高模拟1835.暑假集训CSP提高模拟1936.暑假集训CSP提高模拟2037.暑假集训CSP提高模拟2138.暑假集训CSP提高模拟2239.【LGR-196-Div.4】洛谷入门赛 #2640.暑假集训CSP提高模拟2341.牛客周赛 Round 5642.暑假集训CSP提高模拟2443.暑假集训CSP提高模拟 2544.高一上八月下旬日记45.暑假集训CSP提高模拟2646.暑假集训CSP提高模拟2747.牛客小白月赛99暑假集训CSP提高模拟16
组题人: @Muel_imj
P216. 九次九日九重色
- 部分分
:设 表示当前处理到 的第 位, 的第 位时最大的 ,状态转移方程为 。最终,有 即为所求。
- 正解
-
尝试转化到最长公共子序列上去。
-
发现能对答案产生贡献的只有
的关键点,对关键点的转移只有 有用。 -
记录关键点的位置,树状数组维护前缀
即可,做法同 luogu P4303 [AHOI2006] 基因匹配 。点击查看代码
int p[200010],q[200010],f[200010]; vector<int>pos[200010]; struct BIT { int c[200010]; int lowbit(int x) { return(x&(-x)); } void add(int n,int x,int val) { for(int i=x;i<=n;i+=lowbit(i)) { c[i]=max(c[i],val); } } int getsum(int x) { int ans=0; for(int i=x;i>=1;i-=lowbit(i)) { ans=max(ans,c[i]); } return ans; } }T; int main() { int n,ans=0,i,j; cin>>n; for(i=1;i<=n;i++) { cin>>p[i]; for(j=1;j*p[i]<=n;j++) { pos[j*p[i]].push_back(i); } } for(i=1;i<=n;i++) { cin>>q[i]; } for(i=1;i<=n;i++) { if(pos[q[i]].size()>=1) { for(j=pos[q[i]].size()-1;j>=0;j--)//防止有后效性 { f[pos[q[i]][j]]=T.getsum(pos[q[i]][j]-1)+1;//因为转移过程是单调不降的,所以不用取 max T.add(n,pos[q[i]][j],f[pos[q[i]][j]]); } } } for(i=1;i<=n;i++) { ans=max(ans,f[i]); } cout<<ans<<endl; return 0; }
-
官方题解写的也很抽象,挂一下吧。
-
P217. 天色天歌天籁音
-
部分分
-
:输出 。
-
-
正解
-
懒得复制一遍了,直接粘自己当时写的 题解链接 了。
点击查看代码
int a[200010],b[200010],pos[200010],L[200010],R[200010],ans[200010],num[200010],cnt[200010],klen,ksum; struct ask { int l,r,id; }q[200010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x,int &sum) { num[cnt[a[x]]]--; cnt[a[x]]++; num[cnt[a[x]]]++; sum=max(sum,cnt[a[x]]); } void del(int x,int &sum) { num[cnt[a[x]]]--; sum-=(sum==cnt[a[x]]&&num[cnt[a[x]]]==0); cnt[a[x]]--; num[cnt[a[x]]]++; } int main() { int n,m,l=1,r=0,sum=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(i=1;i<=n;i++) { a[i]=lower_bound(b+1,b+1+b[0],a[i])-b; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l,sum); } while(r<q[i].r) { r++; add(r,sum); } while(l<q[i].l) { del(l,sum); l++; } while(r>q[i].r) { del(r,sum); r--; } ans[q[i].id]=-sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
-
P218. 春色春恋春熙风
- 原题: CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
- 字符重排后可以形成回文串当且仅当至多有一个字符的出现次数为奇数。
- 部分分
: 预处理出任意两点间是否存在「春」,转成 序后查询时等价于查询一个矩阵的最大值,暴力枚举即可。
- 正解
-
考虑将奇偶状压一下,用异或来进行修改和查询。
-
设
表示 到 的路径上的字符形成的状态,那么若 的路径是「春」当且仅当 至多有一位为 ,即最终的结果的只有 。 -
设
表示 到某个节点的路径上的字符形成状态为 时这个节点的最大深度。 -
树上启发式合并维护即可。
点击查看代码
struct node { int nxt,to; }e[1000010]; int head[1000010],c[1000010],ans[1000010],cnt=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } int val(char x) { return x-'a'; } struct DSU_Tree { int fa[1000010],siz[1000010],dfn[1000010],out[1000010],pos[1000010],son[1000010],dep[1000010],sum[1000010],f[(1<<23)+10],tot=0; void init() { memset(f,-0x3f,sizeof(f)); } void add_rt1(int x,int rt) { ans[rt]=max(ans[rt],dep[x]+f[sum[x]]-2*dep[rt]);//此时两点的 lca 就是 rt for(int i=0;i<=21;i++) { ans[rt]=max(ans[rt],dep[x]+f[sum[x]^(1<<i)]-2*dep[rt]); } } void add_rt2(int x) { f[sum[x]]=max(f[sum[x]],dep[x]); } void add_rt(int x,int rt) { add_rt1(x,rt); add_rt2(x); } void del_rt(int x) { f[sum[x]]=-0x3f3f3f3f;//和 memset 赋的值不一样,但无伤大雅 } void add_tree(int x,int rt) { for(int i=dfn[x];i<=out[x];i++) { add_rt1(pos[i],rt); } for(int i=dfn[x];i<=out[x];i++) { add_rt2(pos[i]);//不能同时更新 } } void del_tree(int x) { for(int i=dfn[x];i<=out[x];i++) { del_rt(pos[i]); } } void ask(int x) { for(int i=head[x];i!=0;i=e[i].nxt) { ans[x]=max(ans[x],ans[e[i].to]); } } void dfs1(int x) { tot++; dfn[x]=tot; pos[tot]=x; siz[x]=1; dep[x]=dep[fa[x]]+1; sum[x]=(x!=1)*(sum[fa[x]]^(1<<c[x]));//特判根节点 for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]) { dfs1(e[i].to); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } out[x]=tot; } void dfs2(int x,int flag) { for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=son[x]&&e[i].to!=fa[x]) { dfs2(e[i].to,0); } } if(son[x]!=0) { dfs2(son[x],1); } for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=son[x]&&e[i].to!=fa[x]) { add_tree(e[i].to,x); } } add_rt(x,x); ask(x);//统计子树内部的贡献 if(flag==0) { del_tree(x); } } }T; int main() { int n,i; char pd; cin>>n; for(i=2;i<=n;i++) { cin>>T.fa[i]>>pd; c[i]=val(pd); add(T.fa[i],i); } T.init(); T.dfs1(1); T.dfs2(1,1); for(i=1;i<=n;i++) { cout<<ans[i]<<" "; } return 0; }
-
P219. 雪色雪花雪余痕
-
部分分
:暴搜。
-
正解
- 限制条件移项后得到
,有差分数组单调不降,即差分数组是一个单谷序列。 - 考虑从最小值所在位置及最小值数量入手,不妨考虑最左边的那个位置,然后就分割成了左右两个类似的子问题。对于子问题,原数组求和转化到差分数组求和是一个形如
的形式。 - 设
表示长度为 且和为 的方案数(其中 是 级别的),暴力背包的时间复杂度为 ,无法接受。类似分拆数根号分治后 的求法,将插入若干种数的操作转化为每个数都加 和在开头新加一个 这两种操作,状态转移方程为 ,边界为 。 - 先让最小值为
,此时其数量并不影响最终求和,故可以先对 求前缀和,然后固定左边长度,枚举右边长度的过程中使得总长度 即可,空位(是右边的一段前缀)直接补 。 - 同时,将凸包抬升不改变凸包的性质,可以看做若干次将序列整体加
的完全背包合并过程(直接将方案数算到右边),统计答案时再进行合并。
点击查看代码
const ll p=1000000007; int f[450][100010],sum[450][100010],g[450][100010]; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,s,ans=0,i,j; cin>>n>>m; s=sqrt(2*m+2)+1; f[0][0]=sum[0][0]=1; for(i=0;i<=m;i+=n) { g[0][i]=1; } for(i=1;i<=s;i++) { for(j=0;j<=m;j++) { if(j>=i) { f[i][j]=(f[i][j]+f[i-1][j-i])%p; } if(j>=i*(i+1)/2) { f[i][j]=(f[i][j]+f[i][j-i*(i+1)/2])%p; } g[i][j]=sum[i][j]=(sum[i-1][j]+f[i][j])%p; if(j-n>=0) { g[i][j]=(g[i][j]+g[i][j-n])%p; } } } for(i=0;i<=min(n-1,s);i++) { for(j=0;j<=m;j++) { ans=(ans+1ll*f[i][j]*g[min(n-i-1,s)][m-j]%p)%p; } } cout<<ans<<endl; return 0; }
- 限制条件移项后得到
总结
- 赛时历程:莫名觉得
不可做,所以先开 。 码完怕拿首切没敢立刻交,大样例过了就没管。然后写 的部分分。给 留了 的时间,结果想 的部分分就想了 ,然后在犹豫能不能和最长公共子序列一个做法,改完后发现过样例了就交上去了。 离散化的 写反了,挂了 。
后记
-
貌似是 GalGame 之 9-nine 专场。
-
改后的题面还是很抽象。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18348962,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
2023-08-08 BZOJ3364 Distance Queries 距离咨询 题解