noip模拟9
来自官方题解.md
星际联邦
做法比较多,可以直接用 Prim 来做,但最简单的做法仍然是考虑 Borůvka 算法,我们在每一轮需要找到这个点向外到另一个联通块内的最小边。注意到当 固定时,最小边要么是前缀 的最大值取到的,要么是 内的最小值取到的。我们只需要对每个前缀维护最大值,以及和最大值不在同一个联通块内的最大值,就可以快速求出该联通块向外的最小边。总的时间复杂度为 。
点击查看代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N=3e5+1; const int INF=1e9; int n, a[300005]; LL ans; struct Node{ int val,id; } s[300005]; bool cmp(Node a,Node b) { return a.val < b.val; } signed main() { // freopen("a.in", "r", stdin); freopen("star.in", "r", stdin); freopen("star.out", "w", stdout); cin>>n; for (int i=1;i<=n;i++)cin>>a[i],s[i].id=i,s[i].val=a[i]; sort(s+1,s+1+n,cmp); int j=0; int maxn=-INF; for(int i=1;i<=n;i++) { if(s[i].id<=j) continue; int dmax=-INF; while(j<s[i].id) { j++; if(j==s[i].id) { dmax=max(dmax,a[j]);break; } int del=min(s[i].val-a[j],a[j]-dmax); if (del<a[j]-maxn)dmax=max(dmax,a[j]); else maxn=max(maxn,a[j]),del=a[j]-maxn; ans+=del; } if(i>=2) ans+=s[i].val-maxn; maxn=max(maxn,dmax); } printf("%lld\n",ans); return 0; }
和平精英
首先考虑枚举按位与和按位或的值,假设结果是 ,那么显然所有 都应该放到 or 集合里,所有 都应该放到 and 集合里,如果暴力 check 可以做到 。
考虑再发掘一些性质,考虑枚举 的 popcount,设为 ,那么显然所有 都应该放到 or 集合里,所有 都应该放到 and 集合里, 的把上面的结论拉下来发现这些数应当全部相等,如果暴力 check 可以做到 ,大概有 25pts。
然后考虑直接离线分治回答询问,可以轻松做到 ,如果你不幸写了一个根号复杂度,也可以获得多于暴力的分数。
摆烂合唱
为了方便描述,下面的部分都在表达式树上进行。
分析一下:如果一个变量 (叶结点)的取值影响到了整个表达式(根结点 1)的值,那么必然是 到 1 这条路径上每一个点的值都被影响,所以我们设 表示当 的左/右(对应 或 )子结点的值分别取 0,1 时 的值不同的概率,那么答案就应该是 到 1 这条链上 的乘积。
令 表示随机情况下 点的值为 1 的概率,以 and 型结点, 为例,如果 的值为 0,那么 就不能影响 的取值( 的值总是 0),否则就能影响,所以 。
做 DP 过程中算出 外顺带算出 ,最后 DFS 一次求出一个点到根节点链上的 的积,即可快速得到答案。
时间复杂度为 。
点击查看代码
#include<bits/stdc++.h> using namespace std; const int N=3e5+5,mod=998244353,inv2=((mod+1)>>1); struct Node{int typ,ch[2];}tr[N]; int get(char x) { if(x=='x')return 0; if(x=='&')return 1; if(x=='|')return 2; if(x=='^')return 3; assert(false); } int n,cnt=0,rt,f[N],g[N][2];char s[N<<1]; stack<int>st;stack<pair<int,int>>op; void build() { int len=strlen(s+1); for(int i=1;i<=len;i++) { if(s[i]=='x') st.push(++cnt); if(s[i]=='|'||s[i]=='&'||s[i]=='^') op.push({++cnt,get(s[i])}); if(s[i]==')') { int u=st.top();st.pop(); int v=st.top();st.pop(); pair<int,int>t=op.top();op.pop(); tr[t.first].ch[0]=v;tr[t.first].ch[1]=u; tr[t.first].typ=t.second;st.push(t.first); } } rt=st.top();return; } void dfs(int u) { if(!u)return; if(tr[u].typ==0) { g[u][0]=g[u][1]=inv2; return; } int v0=tr[u].ch[0],v1=tr[u].ch[1]; if(v0)dfs(v0);if(v1)dfs(v1); if(tr[u].typ==1) g[u][0]=(1-1ll*g[v0][1]*g[v1][1]%mod+mod)%mod; if(tr[u].typ==2) g[u][0]=1ll*g[v0][0]*g[v1][0]%mod; if(tr[u].typ==3) g[u][0]=(1ll*g[v0][0]*g[v1][0]%mod+1ll*g[v0][1]*g[v1][1]%mod)%mod; g[u][1]=(1-g[u][0]+mod)%mod;return; } void getans(int u) { if(!u||tr[u].typ==0)return; int v0=tr[u].ch[0],v1=tr[u].ch[1]; if(tr[u].typ==1) { f[v0]=1ll*f[u]*g[v1][1]%mod; f[v1]=1ll*f[u]*g[v0][1]%mod; } if(tr[u].typ==2) { f[v0]=1ll*f[u]*g[v1][0]%mod; f[v1]=1ll*f[u]*g[v0][0]%mod; } if(tr[u].typ==3) { f[v0]=f[v1]=f[u]; } getans(v0);getans(v1);return; } int main() { freopen("binary.in","r",stdin); freopen("binary.out","w",stdout); scanf("%d %s",&n,s+1);build(); dfs(rt);f[rt]=1;getans(rt); for(int i=1;i<=cnt;i++) { if(tr[i].typ==0) printf("%d\n",f[i]); } return 0; }
对称旅行者
考虑先求旅行者 的期望位置,设为 ,那么答案就为 。
当旅行者 旅行时时,由于期望的线性性,,考虑其几何含义,发现是把 关于 和 的中点对称,如果设 ,那么跳第 枚棋子相当于交换 和 。
因此一轮旅行就对应一个 的置换,用类似快速幂的方法就可以求出 轮旅行后的 ,再注意到 始终不变,就可以求出所有棋子的期望位置,时间复杂度为 。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战