noip模拟9

来自官方题解.md

星际联邦

做法比较多,可以直接用 Prim 来做,但最简单的做法仍然是考虑 Borůvka 算法,我们在每一轮需要找到这个点向外到另一个联通块内的最小边。注意到当 i 固定时,最小边要么是前缀 [1,i) 的最大值取到的,要么是 (i,n] 内的最小值取到的。我们只需要对每个前缀维护最大值,以及和最大值不在同一个联通块内的最大值,就可以快速求出该联通块向外的最小边。总的时间复杂度为 O(nlogn)

点击查看代码
#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;
}

和平精英

首先考虑枚举按位与和按位或的值,假设结果是 val,那么显然所有 ai<val 都应该放到 or 集合里,所有 ai>val 都应该放到 and 集合里,如果暴力 check 可以做到 O(nQV)

考虑再发掘一些性质,考虑枚举 val 的 popcount,设为 p,那么显然所有 popcount(ai)<p 都应该放到 or 集合里,所有 popcount(ai)>p 都应该放到 and 集合里,popcount(ai)=p 的把上面的结论拉下来发现这些数应当全部相等,如果暴力 check 可以做到 O(nQlogv),大概有 25pts。

然后考虑直接离线分治回答询问,可以轻松做到 O(nlog2n),如果你不幸写了一个根号复杂度,也可以获得多于暴力的分数。

摆烂合唱

为了方便描述,下面的部分都在表达式树上进行。

分析一下:如果一个变量 i(叶结点)的取值影响到了整个表达式(根结点 1)的值,那么必然是 i 到 1 这条路径上每一个点的值都被影响,所以我们设 f(u,j) 表示当 u 的左/右(对应 j=0j=1)子结点的值分别取 0,1 时 u 的值不同的概率,那么答案就应该是 i 到 1 这条链上 f(u,j) 的乘积。

g(i) 表示随机情况下 i 点的值为 1 的概率,以 and 型结点,j=0 为例,如果 rsi 的值为 0,那么 lsi 就不能影响 i 的取值(i 的值总是 0),否则就能影响,所以 f(i,0)=g(rsi)

做 DP 过程中算出 g(i) 外顺带算出 f(i,0),最后 DFS 一次求出一个点到根节点链上的 f(u,j) 的积,即可快速得到答案。

时间复杂度为 O(n)

点击查看代码
#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;
}

对称旅行者

考虑先求旅行者 i 的期望位置,设为 fi,那么答案就为 fi2mK

当旅行者 i 旅行时时,由于期望的线性性,fi12(2fi1fi+2fi+1fi)=fi1+fi+1fi,考虑其几何含义,发现是把 fi 关于 fi1fi+1 的中点对称,如果设 gi=fi+1fi,那么跳第 i 枚棋子相当于交换 gi1gi

因此一轮旅行就对应一个 1n1 的置换,用类似快速幂的方法就可以求出 K 轮旅行后的 {gi},再注意到 f1 始终不变,就可以求出所有棋子的期望位置,时间复杂度为 O(nlogK)

posted @   ccjjxx  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示