暑假集训CSP提高模拟24
暑假集训CSP提高模拟24
P268.与和
-
下界显然为 ,不妨让 然后进行 。正确性由下一种做法可以进一步推导。点击查看代码
int main() { freopen("and.in","r",stdin); freopen("and.out","w",stdout); ll t,a,s,i; scanf("%lld",&t); for(i=1;i<=t;i++) { scanf("%lld%lld",&a,&s); if(2*a<=s&&((s-a)&a)==a) { printf("Yes\n"); } else { printf("No\n"); } } fclose(stdin); fclose(stdout); return 0; }
-
一个更通用且容易证明的方法是
在二进制表示下 的位置只能出现 在二进制表示下 的位置上,以此来进行 。 的位置不妨都让 这一位为 ,接着便有了上面第一种做法。
点击查看代码
int main() { freopen("and.in","r",stdin); freopen("and.out","w",stdout); ll t,a,s,i; scanf("%lld",&t); for(i=1;i<=t;i++) { scanf("%lld%lld",&a,&s); if(2*a<=s&&((s-2*a)&(~a))==(s-2*a)) { printf("Yes\n"); } else { printf("No\n"); } } fclose(stdin); fclose(stdout); return 0; }
P239.函数
-
部分分
-
:状压。点击查看代码
ll a[200010],b[200010],f[2][(1<<20)+10]; int main() { freopen("func.in","r",stdin); freopen("func.out","w",stdout); ll n,k,maxx,ans=0,s,i,j; cin>>n>>k; for(i=1;i<=n;i++) { cin>>a[i]>>b[i]; } for(s=0;s<=(1<<n)-1;s++) { f[0][s]=1; } for(i=1;i<=k;i++) { for(s=0;s<=(1<<n)-1;s++) { maxx=0; for(j=1;j<=n;j++) { if((s>>(j-1))&1) { maxx=max(maxx,a[j]*f[(i-1)&1][s^(1<<(j-1))]+b[j]); } } f[i&1][s]=maxx; } } for(s=0;s<=(1<<n)-1;s++) { ans=max(ans,f[k&1][s]); } cout<<ans<<endl; fclose(stdin); fclose(stdout); return 0; }
-
-
正解
- 观察到
式子很容易想,但选择顺序貌似很难处理。 - 考虑推式子,假设
,式子展开后有 ,移项后有 。 - 将
按 降序排序,实际排序时将除法转成乘法。 - 设
表示处理到第 个数时选择了 个数的最大值,状态转移方程为 ,边界为 。 - 最终有
即为所求。
点击查看代码
struct node { ll a,b; }c[200010]; ll f[200010][15]; bool cmp(node a,node b) { return a.b*(b.a-1)>b.b*(a.a-1); } int main() { ll n,k,i,j; cin>>n>>k; for(i=1;i<=n;i++) { cin>>c[i].a>>c[i].b; } sort(c+1,c+1+n,cmp); f[0][0]=1; for(i=1;i<=n;i++) { for(j=0;j<=k;j++) { f[i][j]=f[i-1][j]; if(j-1>=0) { f[i][j]=max(f[i][j],c[i].a*f[i-1][j-1]+c[i].b); } } } cout<<f[n][k]<<endl; return 0; }
- 观察到
P238.袋鼠
-
部分分
-
:爆搜。点击查看代码
const ll p=1000000007; ll vis[2010],ans=0; void dfs(ll now,ll prev,ll sum,ll t,ll n) { if(now==t) { ans=(ans+(sum==n))%p; } else { if(prev<now) { for(ll i=1;i<now;i++) { if(vis[i]==0) { vis[i]=1; dfs(i,now,sum+1,t,n); vis[i]=0; } } } else { for(ll i=now+1;i<=n;i++) { if(vis[i]==0) { vis[i]=1; dfs(i,now,sum+1,t,n); vis[i]=0; } } } } } int main() { freopen("kang.in","r",stdin); freopen("kang.out","w",stdout); ll n,s,t,i; cin>>n>>s>>t; for(i=1;i<=n;i++) { if(i!=s) { memset(vis,0,sizeof(vis)); vis[i]=vis[s]=1; dfs(i,s,2,t,n); } } cout<<ans<<endl; fclose(stdin); fclose(stdout); return 0; }
-
-
正解
- 考虑转化题面。
- 问有多少个长度为
的排列 满足 且 同时比 大或同时比 小。 - 比 luogu P2467 [SDOI2010] 地精部落 多了个起点和终点的限制。
- 问有多少个长度为
- 考虑预设性
。 - 设
表示已经填入了 且形成了 个连续段(填入位置不一定是最终位置)的方案,边界为 。 - 对
填入的位置进行分讨。- 新开一个连续段(两端都比
大)- 填入
时只有 个连续段,可填的空有 种情况(头尾需要特判),此时有 。
- 填入
- 连接两个连续段(两端都比
小),同 T2731. DP搬运工1 没有位置硬插进去。- 填入
时只有 个连续段,可填的空有 种情况,此时有 。
- 填入
- 新开一个连续段(两端都比
时只能填在头/尾,有 。- 最终,有
即为所求。
点击查看代码
const ll p=1000000007; ll f[2010][2010]; int main() { freopen("kang.in","r",stdin); freopen("kang.out","w",stdout); ll n,s,t,i,j; cin>>n>>s>>t; f[1][1]=1; for(i=2;i<=n;i++) { for(j=1;j<=i;j++) { if(i!=s&&i!=t) { f[i][j]=((j-(i>s)-(i>t))*f[i-1][j-1]%p+j*f[i-1][j+1]%p)%p; } else { f[i][j]=(f[i-1][j-1]+f[i-1][j])%p; } } } cout<<f[n][1]<<endl; fclose(stdin); fclose(stdout); return 0; }
- 考虑转化题面。
-
组题人的温馨提醒
建议参考 CEOI2016 Kangaroo 线性做法 。
P240.最短路
-
部分分
-
:直接跑最短路,最后再取模。点击查看代码
const ll p=1000000007; struct node { ll nxt,to,x,w; }e[400010]; ll head[400010],dis[400010],vis[400010],u[400010],v[400010],x[400010],cnt=0; void add(ll u,ll v,ll x,ll w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].x=x; e[cnt].w=w; head[u]=cnt; } void dijstra(ll s,ll n) { fill(dis+1,dis+1+n,1e18); memset(vis,0,sizeof(vis)); priority_queue<pair<ll,ll> >q; dis[s]=0; q.push(make_pair(-dis[s],-s)); while(q.empty()==0) { ll x=-q.top().second; q.pop(); if(vis[x]==0) { vis[x]=1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w) { dis[e[i].to]=dis[x]+e[i].w; q.push(make_pair(-dis[e[i].to],-e[i].to)); } } } } } int main() { freopen("hellagur.in","r",stdin); freopen("hellagur.out","w",stdout); ll n,m,s,t,flag=1,i; cin>>n>>m; for(i=1;i<=m;i++) { cin>>u[i]>>v[i]>>x[i]; flag&=(x[i]<=30); } cin>>s>>t; if(flag==1) { for(i=1;i<=m;i++) { add(u[i],v[i],x[i],(1ll<<x[i])); add(v[i],u[i],x[i],(1ll<<x[i])); } dijstra(s,n); if(dis[t]==1e18) { cout<<-1<<endl; } else { cout<<dis[t]%p<<endl; } } else { cout<<"-1"<<endl; } fclose(stdin); fclose(stdout); return 0; }
-
:观察到 互不相同,在最短路的更新过程一定不会产生进位(若产生了说明这条边重复走过,显然不优),且比较时只按二进制下最高位比较即可。具体实现时用二进制表示到每个点的最短路长度,可能需要一些诸如拿主席树来存等方法来保证空间复杂度正确,一定程度上算启发正解了(?)。
-
-
正解
-
上次遇到高精度最短路还是 2022年普及组3(因编号错误实际应该是2022年普及组2) T4 雷 。
-
考虑保存最短路在二进制表示下的每一位,空位补
。 -
因为每个节点的最短路长度仅会在松弛操作时进行更改,考虑主席树来节省空间。
-
这样的话在
的过程中需要支持相加和比较。- 相加
- 加上
等价于从 向左找到一个最靠左的位置 使得 都为 ,接着让 推平成 ,让 加一。- 找
- 每个节点存一下当前区间内
的个数,然后在线段树上二分即可。
- 每个节点存一下当前区间内
- 区间推平
- 由于只有区间推平成
操作考虑初始时建出一棵二进制每一位为 的主席树。推平时将其连到这棵主席树上。
- 由于只有区间推平成
- 找
- 新建一棵临时主席树存相加后的值进行比较。
- 加上
- 比较
- 考虑哈希维护一段区间的数值来判断是否相等,找到两个数值最高不相同的位(叶子节点),然后进行比较。线段树上二分即可。
- 相加
-
类似建最短路树一样记录路径。
点击查看代码
const int mod=1000000007,base=2; struct node { int nxt,to,x; }e[400010]; int head[400010],vis[400010],cnt=0; ll mi[400010]; void add(int u,int v,int x) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].x=x; head[u]=cnt; } struct PDS_SMT { int root[400010],rt_sum; struct SegmentTree { int ls,rs,sum,len; ll hsh; }tree[400010<<5]; #define lson(rt) tree[rt].ls #define rson(rt) tree[rt].rs int build_rt() { rt_sum++; return rt_sum; } void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; tree[rt].hsh=(tree[lson(rt)].hsh+tree[rson(rt)].hsh*mi[tree[lson(rt)].len]%mod)%mod;//为方便代码书写,二进制下的左右与题解中所说的正好相反 } void build_tree(int &rt,int l,int r) { rt=build_rt(); tree[rt].len=r-l+1; if(l==r) { return; } int mid=(l+r)/2; build_tree(lson(rt),l,mid); build_tree(rson(rt),mid+1,r); pushup(rt); } void update1(int pre,int &rt,int l,int r,int pos) { rt=build_rt(); tree[rt]=tree[pre]; if(l==r) { tree[rt].sum++; tree[rt].hsh++; return; } int mid=(l+r)/2; if(pos<=mid) { update1(lson(pre),lson(rt),l,mid,pos); } else { update1(rson(pre),rson(rt),mid+1,r,pos); } pushup(rt); } void update2(int init,int pre,int &rt,int l,int r,int x,int y) { if(x<=l&&r<=y) { rt=init; return; } rt=build_rt(); tree[rt]=tree[pre]; int mid=(l+r)/2; if(x<=mid) { update2(lson(init),lson(pre),lson(rt),l,mid,x,y); } if(y>mid) { update2(rson(init),rson(pre),rson(rt),mid+1,r,x,y); } pushup(rt); } int query(int rt,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt),mid+1,r,x,y); } return ans; } int find(int rt,int l,int r,int pos) { if(l==r) { return l; } int mid=(l+r)/2; if(pos>mid) { return find(rson(rt),mid+1,r,pos); } if(query(lson(rt),l,mid,pos,mid)==mid-pos+1) { return find(rson(rt),mid+1,r,mid+1); } else { return find(lson(rt),l,mid,pos); } } void add(int pre,int &rt,int pos) { int y=find(pre,0,200001,pos); if(pos<=y-1) { update2(root[0],pre,rt,0,200001,pos,y-1); } else//如果找不到 { rt=pre; } update1(rt,rt,0,200001,y); } bool cmp(int rt1,int rt2,int l,int r) { if(l==r) { return tree[rt1].sum>tree[rt2].sum;//在叶子节点比较 } int mid=(l+r)/2; if(tree[rson(rt1)].hsh==tree[rson(rt2)].hsh) { return cmp(lson(rt1),lson(rt2),l,mid); } else { return cmp(rson(rt1),rson(rt2),mid+1,r); } } }T; struct quality { int id,rt; bool operator < (const quality &another) const { return T.cmp(rt,another.rt,0,200001); } }; void dijkstra(int s,int n) { T.build_tree(T.root[0],0,200001); T.root[s]=T.root[0]; priority_queue<quality>q; q.push((quality){s,T.root[s]}); while(q.empty()==0) { int x=q.top().id; q.pop(); if(vis[x]==0) { vis[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { T.add(T.root[x],T.root[n+1],e[i].x);//临时主席树 if(T.root[e[i].to]==0||T.cmp(T.root[e[i].to],T.root[n+1],0,200001)==true) { T.root[e[i].to]=T.root[n+1]; q.push((quality){e[i].to,T.root[e[i].to]}); } } } } } int main() { freopen("hellagur.in","r",stdin); freopen("hellagur.out","w",stdout); int n,m,u,v,x,s,t,i; cin>>n>>m; for(i=1;i<=m;i++) { cin>>u>>v>>x; add(u,v,x); add(v,u,x); } cin>>s>>t; for(i=0;i<=200001;i++) { mi[i]=(i==0)?1:mi[i-1]*base%mod; } dijkstra(s,n); if(vis[t]==0)//未被加入优先队列 { cout<<"-1"<<endl; } else { cout<<T.tree[T.root[t]].hsh<<endl; } fclose(stdin); fclose(stdout); return 0; }
-
P269. 赤(red)
总结
结束前 把原状压改成了爆搜想节省空间,结果挂了 。 结束前 发现把freopen("hellagur.out","w",stdout);
写成了freopen("hellagur.out","r",stdin);
。- 感觉自己偏随机化和乱搞做法的题不是很敢写,明知没有正确性的做法被自己
掉就没尝试写过看实际能拿多少分。
后记
-
赛时多次出锅,包括但不限于对负数进行逻辑与运算, 写反,以为非负整数 。- 逻辑与运算仅在非负整数中有定义。
-
组题人貌似太高估我们了。 -
原属于 HZOI2024 冲刺 NOIP2024 400pts 计划 2.图论专题 A ,结果被搬到模拟赛了。 -
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18367260,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
2023-08-19 普及模拟2 +【LGR-155-Div.3】洛谷基础赛 #3 &「NnOI」Round 2