CSP模拟50

T1 异或

赛时 \(8\) min 切了。


\[\sum\limits_{i=0}^{n-1} popcount(i\oplus (i+1)) \]

\(a_i=popcount(i\oplus (i-1))\),打个表可以发现 \(a_{[1,2^i]}\)\(a_{[2^i+1,2^{i+1}]}\) 之间的关系,即 \(\forall j\in[1,2^i-1],a_{j+2^i}=a_j\),同时 \(a_{2^{i+1}}=a_{2^i}+1\)

然后发现还挺对的,因为 \(\forall j\in[1,2^i-1]\),与 \(j+2^i\) 在二进制上的区别只有第 \(i\) 位(从第 \(0\) 位开始),但 \(j\)\(j-1\) 的第 \(i\) 位都是 \(0\)\(j+2^i\)\(j+2^i-1\) 的第 \(i\) 位都是 \(1\),所以 \(a_{j+2^i}=a_j\)

同时对于 \(a_{2^i}\),发现等于 \(i+1\),挺显然的,所以后者也是对的。

所以 \(\sum\limits_{j=1}^{2^{i+1}}a_j=2\sum\limits_{j=1}^{2^i}a_j+1\)

所以直接遍历 \(n\) 每一二进制位,为 \(1\) 的二进制位加上前缀贡献,时间复杂度 \(O(\log n)\)

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
long long n,cur,ans;
int main()
{
#ifdef ONLINE_JUDGE
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
#endif
    cin>>n;
    while(n)
    {
        cur=cur*2+1;
        if(n&1) ans+=cur;
        n>>=1;
    }
    cout<<ans<<'\n';
    return 0;
}

T2 赌神

开始以为是什么牛逼博弈论直接跳了,后来发现后俩题做不出来回来一看诈骗题。

所以为啥赌博题能研究出来必定不亏啊?所以赌博的猫腻就在于他不能拥有足够多的单位注导致黑手操作使他必定亏吗?

好像本来也不会知道各种颜色球的个数,那没事了。


先要求玩家使用一种策略,记\(a_i\) 表示第 \(i\) 个球剩余了多少个,\(S=\sum\limits_{i=1}^n a_i\)。对于 \(\forall i\in[1,n]\) 下注 \(\text{现有本金}\times \dfrac{a_i}{S}\),那么这轮如果落下第 \(i\) 个球,下轮本金就变为这轮本金的 \(\dfrac{a_i}{S}\times n\) 倍。

发现这样很优,因为这样每次无论落下哪个球最终获得的收益是一定的,黑手无论怎样操作,最后总收益都是 \(\dfrac{\prod\limits_{i=1}^n a_i!}{S!}\times n^S\),相当于是乘法交换律。

然后证明这样最优,如果不这样操作,那么就肯定有至少一种球获得的收益要比原来低,相当于给了黑手操作空间,他会让这类球掉下来。但你发现你这样做并没任何作用,因为去掉这个球后变成了一个完全一样的子问题,也就是说你之后也不会比原来那种策略更优,最多也只是一样。所以肯定不会变优,故原来策略最优。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1e6+10,MOD=998244353;
int n,a[MAXN],sum;long long P[MAXN],ans=1;
inline long long ksm(long long a,int b)
{
    long long ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%MOD;
        a=a*a%MOD,b>>=1;
    }
    return ans;
}
int main()
{
#ifdef ONLINE_JUDGE
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
#endif
    cin>>n;P[0]=1;
    for(register int i=1;i<=1000000;++i) P[i]=P[i-1]*i%MOD;
    for(register int i=1;i<=n;++i)
        cin>>a[i],sum+=a[i],ans=ans*P[a[i]]%MOD;
    ans=ans*ksm(P[sum],MOD-2)%MOD*ksm(n,sum)%MOD;
    cout<<ans<<'\n';return 0;
}

T3 路径

不会,不想学,只会 \(70 pts\),干脆不写了。

原题-P4827 [国家集训队] Crash 的文明世界

T4 树

为啥模拟赛考 P7710 [Ynoi2077] stdmxeypz 啊。

Subtree distance modulo x equal y plus z

还挺有意思的,做法好像很多,但好像又大同小异。

主要就是根号分治,赛时想到了,但只想着 \(x\) 小时怎么从 \(x\) 大的基础上优化,所以分治不彻底,不触及根本,充分体现了 int_R 的软弱性与妥协性。

赛时只想到 \(O(n\sqrt n\log n)\),目测 \(10^5\) 过不去(虽然好像可以?),写了个完全可以被卡的没复杂度保证的线段树结果过了 \(10^5\),拿了 \(60\) pts。

然后去看了 Ynoi 题解写了正解其一,但没注意到那篇题解说要卡常,于是乎 HZOJ 过了,洛谷过不了。

代码写了好长,不如 5k 做法。


我:卡常没意义,不卡了。

5k:lxl 说过数据结构的本质就是卡常,你说卡常没有意义,就是说数据结构没有意义。

我:……

我:没事我放着,万一哪天……我不想写题了来卡卡常也是很好的。

5k:人话——咕了。

我:……

为啥今天写总结废话咋这么老多,虽然这句也是废话。


1 a x y z:把 \(a\) 子树中所有与 \(a\) 的距离模 \(x\) 等于 \(y\) 的节点权值加 \(z\)

2 a:查询 \(a\) 节点的权值。

1 操作变为 \(a\) 子树中所有深度模 \(x\) 等于 \((y+dep_a)\bmod x\) 的节点权值加 \(z\) ,这样要好看一些。

子树,所以求出 dfs 序转化到序列上,然后考虑对 \(x\) 根号分治。

对于 \(x\) 小于等于 \(\sqrt n\),将每个询问拆成 \(\sqrt n\) 个。

具体的,先枚举 \(x:1\to \sqrt n\),再枚举余数 \(r:0\to x-1\),处理所有为 \(\operatorname{mod} x=r\) 形式的修改和 \(dep_a \bmod x=r\) 的所有询问。相当于区间加,单点查。

这一部分每个修改只执行 \(1\) 次,但每个询问需要处理 \(\sqrt n\) 次,平衡一下要求 \(O(\sqrt n)\) 区间加,\(O(1)\) 单点查,分块直接做。

对于 \(x\) 大于 \(\sqrt n\),将每个修改拆成 \(\sqrt n\) 个。

具体的,一个修改的形式是对于一些深度的点的,由于 \(x>\sqrt n\) ,所以这些深度的个数是 \(\sqrt n\) 级别的。于是可以将修改拆成 \(\sqrt n\) 个。也就是枚举深度 \(d:1\to n\),对于所有 \(d\bmod x=y\) 的修改操作和所有 \(dep_a=d\) 的查询操作进行处理,同样相当于区间加,单点查。

只不过这一部分每个修改执行 \(\sqrt n\) 次,每个询问处理 \(1\) 次,平衡一下要求 \(O(1)\) 区间加,\(O(\sqrt n)\) 单点查,差分一下仍然分块直接做。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<vector>
using namespace std;
const int MAXN=3e5+10,MAXT=550;
int n,q,T,to[MAXN<<1],nxt[MAXN<<1],head[MAXN],cur;
int dfn[MAXN],dep[MAXN],siz[MAXN],tot,ANS[MAXN];
struct node{int pos,a,x,y,z;}p[MAXN];
vector <node> v[MAXN];
namespace part
{
    int len,bel[MAXN],a[MAXN],sum[MAXT],lazy[MAXT],b[MAXT],e[MAXT];
    inline void init()
    {
        len=sqrt(n);
        for(register int i=1;i<=n;++i) bel[i]=(i-1)/len+1;
        for(register int i=1;i<=bel[n];++i)
            b[i]=(i-1)*len+1,e[i]=min(i*len,n);
        return ;
    }
    inline void change(int l,int r,int z)
    {
        if(bel[l]==bel[r])
        {
            for(register int i=l;i<=r;++i) a[i]+=z,sum[bel[i]]+=z;
            return ;
        }
        for(register int i=l;i<=e[bel[l]];++i) a[i]+=z,sum[bel[i]]+=z;
        for(register int i=b[bel[r]];i<=r;++i) a[i]+=z,sum[bel[i]]+=z;
        for(register int i=bel[l]+1;i<bel[r];++i) lazy[i]+=z;
        return ; 
    }
    inline int query(int l,int r)
    {
        int ans=0;
        if(bel[l]==bel[r])
        {
            for(register int i=l;i<=r;++i) ans+=a[i]+lazy[bel[i]];
            return ans;
        }
        for(register int i=l;i<=e[bel[l]];++i) ans+=a[i]+lazy[bel[i]];
        for(register int i=b[bel[r]];i<=r;++i) ans+=a[i]+lazy[bel[i]];
        for(register int i=bel[l]+1;i<bel[r];++i) ans+=sum[i]+lazy[i]*(e[i]-b[i]+1);
        return ans;
    }
};
inline void add(int x,int y)
{
    to[++cur]=y,nxt[cur]=head[x];
    head[x]=cur;return ;
}
void dfs(int x,int fa=0)
{
    dfn[x]=++tot,dep[x]=dep[fa]+1,siz[x]=1;
    for(register int i=head[x];i;i=nxt[i])
    {
        int y=to[i];if(y==fa) continue;
        dfs(y,x);siz[x]+=siz[y];
    }
    return ;
}
int main()
{
#ifdef ONLINE_JUDGE
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
#endif
    cin>>n>>q;T=sqrt(n);
    for(register int i=1;i<n;++i)
    {
        int x,y;cin>>x>>y;
        add(x,y),add(y,x);
    }
    dfs(1),part::init();
    for(register int i=1;i<=q;++i)
    {
        int opt,a,x=0,y=0,z=0;cin>>opt>>a;
        if(opt==1){cin>>x>>y>>z,y=(y+dep[a])%x;}
        p[i]={i,a,x,y,z};
    }
    for(register int i=1;i<=T;++i)
    {
        for(register int j=1;j<=q;++j)
        {
            if(p[j].x==i) v[p[j].y].push_back(p[j]);
            else if(!p[j].x) v[dep[p[j].a]%i].push_back(p[j]);
        }
        for(register int j=0;j<i;++j)
        {
            for(node k:v[j])
            {
                if(k.x) part::change(dfn[k.a],dfn[k.a]+siz[k.a]-1,k.z);
                else ANS[k.pos]+=part::query(dfn[k.a],dfn[k.a]);
            }
            for(node k:v[j])
                if(k.x) part::change(dfn[k.a],dfn[k.a]+siz[k.a]-1,-k.z);
            v[j].clear();
        }
    }
    for(register int j=1;j<=q;++j)
    {
        if(p[j].x>T)
            for(register int i=p[j].y;i<=n;i+=p[j].x) v[i].push_back(p[j]);
        else if(!p[j].x) v[dep[p[j].a]].push_back(p[j]);
    }
    for(register int i=1;i<=n;++i)
    {
        for(node k:v[i])
        {
            if(k.x)
            {
                part::change(dfn[k.a],dfn[k.a],k.z);
                part::change(dfn[k.a]+siz[k.a],dfn[k.a]+siz[k.a],-k.z);
            }
            else ANS[k.pos]+=part::query(1,dfn[k.a]);
        }
        for(node k:v[i])
            if(k.x)
            {
                part::change(dfn[k.a],dfn[k.a],-k.z);
                part::change(dfn[k.a]+siz[k.a],dfn[k.a]+siz[k.a],k.z);
            }
    }
    for(register int i=1;i<=q;++i) if(!p[i].x) cout<<ANS[i]<<'\n';
    return 0;
}
posted @ 2023-10-07 20:42  int_R  阅读(71)  评论(1)    收藏  举报