• [Contest on 2022.6.28] 写一道挂一道


    \(\cal T_1\) 基础概率练习题 / probability

    Description

    对于一个长度为 \(n\) 的非负整数序列 \(a_{1}, a_{2}, \ldots, a_{n}\),已知 \(a_{1} \geqslant k, \sum_{i=1}^{n} a_{i}=m\),询问 \(a_{1}\) 是最大值的概率,如果序列中有多个最大值,随机任意一个作为最大值。对 \(998244353\) 取模。

    \[\begin{array}{|c|c|c|c|c|} \hline 子任务编号 & n & m & k & 分数 \\ \hline 1 & & & =0 & 5 \\ \hline 2 & \leqslant 10 & \leqslant 10 & & 5 \\ \hline 3 & \leqslant 100 & \leqslant 5000 & & 20 \\ \hline 4 & \leqslant 5000 & \leqslant 5000 & & 20 \\ \hline 5 & \leqslant 5000 & & & 20 \\ \hline 6 & & & & 30 \\ \hline \end{array} \]

    对于所有数据,\(1 \leqslant n, m, k \leqslant 10^{7}, k \leqslant m\).

    Solution

    考场上想了一个 \(n,m\leqslant 5000\) 的做法,在这里讲一下吧 。

    首先想到枚举 \(a_1\) 的值为 \(c\),然后计算最大值为 \(c\),最大值有 \(i+1\) 个的排列数量。那么就需要将 \(m-(i+1)c\) 分给 \(n-i-1\) 个数,求每个数小于 \(c\) 的方案数,这个可以用二项式反演解决,设 \(f(j)\) 为将 \(m-(i+1)c\) 分给 \(n-i-1\) 个数,至少\(j\) 个数大于等于 \(c\) 的方案数,\(g(j)\) 就是恰好。

    调了好久,最后发现是 long long 乘爆了。。


    正解做法出奇的简单。考虑条件概率 \(P(A | B) = P(AB)/P(B)\),设第一个人拿到 rk1 为 A,设第一个人得分 \(\geqslant k\) 为 B,于是只需要求 \(P(AB)\)\(P(B)\).

    \(P(B)\) 非常好计算,对于 \(P(AB)\),也就是第一个人 rk1 且得分 \(\geqslant k\) 的概率。事实上可以发现,对于每个人这个概率是相同的,并且只要有人得分 \(\geqslant k\) 就意味着 rk1 得分一定 \(\geqslant k\)

    于是只用算出至少一个人的得分 \(\geqslant k\) 的方案数就可以得到 \(nP(B)\)(把这个问题反一下,就可以用二项式反演 \(\mathcal O(n)\) 做),从而得到答案。

    Code

    懒得写了。
    

    \(\cal T_2\) 基础树剖练习题 / chain

    Description

    给定一棵 \(n\) 个点以 \(1\) 为根的有根树,边有颜色,初始时全为白色。接下来有三种操作:

    • 1 u:表示修改 \(u\) 到根路径上的边的颜色,具体修改方式在下文说明;

    • 2 u:表示查询 \(u\) 到根路径上的黑边的个数;

    • 3 u:表示查询 \(u\) 子树内黑边的个数。

    修改方式:首先,将从 \(u\) 到根的链拉出来,即为 \(l i s_{1}, l i s_{2}, \ldots, l i s_{m}\),其中 \(l i s_{1}=u, l i s_{m}= 1\)。然后,除去 \(u\) 到根路径上的边,将其余与 \(lis _{1} , lis _{3} , lis _{5}, \ldots\) 相连的边染成黑色,将与 \({lis}_{2} , lis _{4} , lis _{6}, \ldots\) 相连的边染成白色。最后,对于在 \(u\) 到根路径上的边,将 \(\left(\right. lis_1, \left.l i s_{2}\right),\left(l i s_{3}, l i s_{4}\right),\left(l i s_{5}, l i s_{6}\right), \ldots\) 染成黑色,将 \(\left(\right. lis _{2} , \left.lis_{3}\right),\left(\right. lis _{4} , \left.lis_{5}\right),\left(\right. lis_{6} , \left.lis_{7}\right), \ldots\) 染成白色。

    \(1\leqslant n,q\leqslant 10^5\).

    Solution

    一些闲话:考试的时候写了基础暴力和链的部分分,结果最后所有暴力都跑过了一个理论跑不过的测试点,链的测试点却压根没有,真的亏麻了(。

    以下文本需要在 此网站 中进行解密,密码是 恒电平逻辑电路 的英文名后面接上其中文名的拼音全称:

    U2FsdGVkX19OSAbz1MTNf6P3p4zDyhJwzTRB3R+B8KQdKTF+lZccKzEy7MOdSCbJ
    6zaqr8iy8D4UCxjglc1hcdKSFRC4nR8xC4N/F5bpgULg1l4waOmV8vPeHNUBSaIq
    QXXCp1ezsIq6J0NP+qB/F917ZcrYIRU+/dtesWHKtqHrW/F7Ozjr6JhJCLv9BC4Q
    eTRVX9KB4adZb1/nadcIGe4DyLfsd0GIs3wNt7o6nYZEc2k1on4feSgr14j/qBMv
    fwYXWdYE8lKD/jAIMF6bgP5qms5LO5CkIC7xedPALM5YL9wFluQTNa1GCBbrjM34
    cgZowmyOXmjuEDE+4N8tOhJNYEBrGxcwvAeZJie3yEwZbrXsPxtYZpy6GN9vVbPY
    kLed6+f6EBWJUYV3IBBF4nFExcet3k470nM7X7SViBeTTuDBhmIKmXpInlGBGhca
    01WtSVDo325BCWfuj65CiXgK4J87b+U4umWjHJY+3uJgmKoJJddFFhALSCv4owHG
    fUG/d4QjsAnue47YL6HkqrQFUgZQVTBtQyv4PgTLr2vpDA9j2EJXFBMlr1Ve6puv
    0SnAssUv8Xu9QcQEgWpKwjWVoUgG/xXhy1i0D5qJ6U8FB055F6bTczS+jkdf356q
    dHnMZ3KKDG8Z5mAbO3hqcvJJ/sRQyMNgUT/XiuSoJdhGYAOxQvQbXEwlXc61lzys
    cQ19ksRwtoJ+jy8kLFrQIOOILM/dv48WVBUgcfFz4T8ko/irLl+NKGLL7v8/Gg+v
    2yF/kRR/YUVBvlZoE8OJFbutgWiqu8Wa+uIEq+q05ODT0ylDKDX5DzET9JmkZ9sR
    a/sqMGLDhGl36HzIRZJ+qZvr6dQgnR5Q8mRBi9qhXBgfpcQbdQuTu1iA0omhut4Y
    iNeQkJ6a6/HBYN+KHWh+hrKfFoq4x1iFp2R1ARmZX43zb0Z5DcENDIKWARg/t0Dn
    OkDvDn4AAKCpeUisLPzbhy8GzbxUEGoValiKRMk4qJkO7AjJtt2pkiWxIAs2kaU7
    JAwVdJUklKtlcQ/fKcSUl8ZG5BqDGwcV2BHRA1Yr7sSHrKEs18kKsS8BqDOsvbS4
    es1rYHHht/o7+22rQJG03J5ck5jj+8rS2DZdbrS0TPsy9KiZPTO/sYxxkDsgSLJW
    +3BKtZb05m0mH5pFFrkv319/8AFfsTMxaP696j4kKOn6wk31edvuMpSnllED0Xhk
    l93gZbaDUQh6QGUXu5CTpvnR989VMEqtuUy8DF7qeD7/GZOSy3SNpS04hvBrcWCj
    Sa5ZL8asj0hjgKzqtzFbZsKkexcHWWWb+eEzeY/rjXMWRD9CEUVgp3jbp//6+tqH
    cENA4F1UU9RN1NK0ZwlgCgVdoxoK+Gi2OGso8ByD1+i0dy/Wki6cSCJjv1pOBd0k
    kwbvSmrD1HQ9zKiqIky4Cq+b5QMZuKHcSQJtus3rIjQYrorS/oRlhKgFhPH6k4Ef
    f2UGu5jexB/Oc0gp7cEIJYPd0lHq+NeW0Oow4ifbsaSEShMV2CIbs6x3cXNmQX4U
    Up7dweO8dFrkmI8dRDPPlSoGMYNyBkX23YK6pu/wLVaekAa3796ywFkZzYNg8Q6q
    DX5wsH2Gk/xRSVfMXrGwr14U/U8IxK7TpJvh3hqpEwSHtUDdCeXluZ/U4ZDlGn0c
    weDAir1vs5EULafW/oCzP2NyeDeGUSJ/MV+de1JweAtJJa7ploB1GolI/jgNBvsu
    GJeeg1I2/eYdW+ifPhufR2alaZFSIIbPyXUK0dEyZIUU4l0kbhZUer8ji+GfCu1L
    IrunDvO+Ik3mOiI9cfG+FWwWfDiIyE1Vdx10SFMnCd8ErT0Zg6PFH5gmFkfTBenE
    PMrCk3UIWXBBk0LV2jIvwA==
    

    回到本题,用点 \(u\) 表示 \(u\) 到父亲的那条边。直接根据询问修改是不易的,但是你会发现一条边的黑白,只和被修改节点深度的奇偶性有关,所以可以直接预处理某个奇偶性下黑边条数,修改的时候就只需要改 \(\rm tag\),具体可以看代码实现。复杂度 \(\mathcal O(n\log^2 n)\).

    Code

    这里放一个链部分分的代码,已经和正解对拍过了:

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
        T x=0; char s; bool f=0;
        while(!isdigit(s=getchar())) f|=(s=='-');
        for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
        return f? -x: x;
    }
    template <class T>
    inline void write(T x) {
        static int writ[50], w_tp=0;
        if(x<0) putchar('-'), x=-x;
        do writ[++w_tp]=x-x/10*10, x/=10; while(x);
        while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    # include <vector>
    using namespace std;
    
    const int maxn = 1e5+5;
    
    vector <int> e[maxn];
    int n, f[maxn], val[maxn];
    
    bool isChain() {
    	for(int i=2;i<=n;++i)
    		if(f[i]!=i-1) return false;
    	return true;
    }
    
    namespace Jinx {
    	int add[maxn];
    	int delta, stk[maxn], tp, del[maxn];
    	int query(int u) {
    		if(!tp) return 0;
    		if(u>stk[1]) return (stk[1]>>1)+delta+1;
    		int l=1, r=tp, mid, ans=-1;
    		while(l<=r) {
    			mid = l+r+1>>1;
    			if(stk[mid]>=u) ans=mid, l=mid+1;
    			else r=mid-1;
    		}
    		if((u&1)==(stk[ans]&1)) mid=u>>1;
    		else mid = (u-1>>1);
    		return delta-del[ans]+mid;
    	}
    	void Rux() {
    		for(int q=read(9); q; --q) {
    			int opt=read(9), u=read(9);
    			if(opt==1) {
    				while(tp && stk[tp]<=u) delta -= add[tp--];
    				if(!tp) { stk[++tp]=u, delta=0; continue; }
    				stk[++tp]=u;
    				if((u&1)==(stk[tp-1]&1)) add[tp]=1;
    				else add[tp] = ((u&1)?0:1);
    				delta+=add[tp], del[tp] = delta;
    			} else if(opt==2) print(query(u),'\n');
    			else print(query(n)-query(u),'\n');
    		}
    	}
    }
    
    int query(int u) {
    	int ret=0;
    	for(const auto& v:e[u]) 
    		ret += query(v)+val[v];
    	return ret;
    }
    
    int main() {
    	n=read(9);
    	for(int i=2;i<=n;++i) 
    		e[f[i]=read(9)].emplace_back(i);
    	if(isChain()) return Jinx::Rux(), (0-0);
    	for(int q=read(9); q; --q) {
    		int opt=read(9), u=read(9);
    		if(opt==1) {
    			int pre1=0;
    			for(int x=u; x; ) {
    				for(const auto& i:e[x])
    					if(i^pre1) val[i]=1;
    				if(x^u) val[pre1]=0;
    				int v = f[x];
    				if(!v) break;
    				for(const auto& i:e[v])
    					if(i^x) val[i]=0;
    				val[x]=1, x=f[v], pre1=v; 
    			}
    		} else if(opt==2) {
    			int ans=0;
    			for(int x=u; x^1; x=f[x])
    				ans += val[x];
    			print(ans,'\n');
    		} else print(query(u),'\n');
    	}
    	return 0;
    }
    

    正解代码:

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
        T x=0; char s; bool f=0;
        while(!isdigit(s=getchar())) f|=(s=='-');
        for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
        return f? -x: x;
    }
    template <class T>
    inline void write(T x) {
        static int writ[50], w_tp=0;
        if(x<0) putchar('-'), x=-x;
        do writ[++w_tp]=x-x/10*10, x/=10; while(x);
        while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    # include <vector>
    using namespace std;
    
    const int maxn = 1e5+5;
    
    vector <int> e[maxn];
    int las[maxn];
    int Ref[maxn], lst[maxn], rst[maxn], ls[maxn], rs[maxn];
    int f[maxn], n, dep[maxn], son[maxn], tp[maxn], dfn[maxn];
    
    namespace caterpillar {
        int sz[maxn], idx;
        void predfs(int u) {
            dep[u] = dep[f[u]]+(sz[u]=1);
            for(const auto& v:e[u]) {
                predfs(v), sz[u]+=sz[v];
                if(sz[son[u]]<sz[v]) son[u]=v;
            }
        }
        void handleSons(int u) {
            ls[u] = idx+1;
            for(const auto& v:e[u])
                if(v^son[u]) Ref[dfn[v]=++idx]=v;
            rs[u] = idx;
            if(son[u]) handleSons(son[u]);
            las[u] = idx;
        }
        void relabel(int u,int t) {
            tp[u]=t;
            if(!dfn[u]) Ref[dfn[u]=++idx]=u;
            if(u==t) handleSons(u);
            lst[u] = idx+1;
            if(son[u]) relabel(son[u],t);
            for(const auto& v:e[u])
                if(v^son[u]) relabel(v,v);
            rst[u] = idx;
        }
    }
    
    namespace SgT {
        struct node { int s,tag,dat[2]; } t[maxn<<2];
        void _pushUp(int o) {
            t[o].dat[0] = t[o<<1].dat[0]+t[o<<1|1].dat[0],
            t[o].dat[1] = t[o<<1].dat[1]+t[o<<1|1].dat[1];
        }
        void pushUp(int o) { t[o].s=t[o<<1].s+t[o<<1|1].s; }
        void pushDown(int o) {
            if(t[o].tag==-1) return;
            t[o<<1].tag = t[o<<1|1].tag = t[o].tag,
            t[o<<1].s = t[o<<1].dat[t[o].tag],
            t[o<<1|1].s = t[o<<1|1].dat[t[o].tag];
            t[o].tag = -1;
        }
        void build(int o,int l,int r) {
            t[o].tag = -1;
            if(l==r) return t[o].dat[dep[Ref[l]]&1]=1, void();
            int mid = l+r>>1; build(o<<1,l,mid);
            build(o<<1|1,mid+1,r), _pushUp(o);
        }
        void modify(int o,int l,int r,int L,int R,int k) {
            if(L>R) return;
            if(l>=L && r<=R) return t[o].s=t[o].dat[t[o].tag=k], void();
            int mid = l+r>>1; pushDown(o);
            if(L<=mid) modify(o<<1,l,mid,L,R,k);
            if(R>mid) modify(o<<1|1,mid+1,r,L,R,k); pushUp(o);
        }
        int query(int o,int l,int r,int L,int R) {
            if(L>R) return 0;
            if(l>=L && r<=R) return t[o].s;
            int mid = l+r>>1, ret=0; pushDown(o);
            if(L<=mid) ret=query(o<<1,l,mid,L,R);
            if(R>mid) ret=ret+query(o<<1|1,mid+1,r,L,R);
            return ret;
        }
    }
    
    void Modify(int u) {
        int k = (dep[u]&1), las=0;
        for(; u; u=f[las=tp[u]]) {
            SgT::modify(1,1,n,lst[tp[u]],dfn[u],k);
            SgT::modify(1,1,n,ls[tp[u]],rs[u],k^1);
            if(las)	SgT::modify(1,1,n,dfn[las],dfn[las],k);
            if(son[u]) SgT::modify(1,1,n,dfn[son[u]],dfn[son[u]],k^1);
            if(tp[u]^1) SgT::modify(1,1,n,dfn[tp[u]],dfn[tp[u]],k);
        }
    }
    
    int handleChain(int u,int ret=0) {
        for(; u; u=f[tp[u]]) ret += SgT::query(1,1,n,lst[tp[u]],dfn[u])+
            SgT::query(1,1,n,dfn[tp[u]],dfn[tp[u]]); return ret;
    }
    
    int handleSubtree(int u) {
        return SgT::query(1,1,n,ls[u],las[u])+SgT::query(1,1,n,lst[u],rst[u]);
    }
    
    int main() {
        freopen("chain.in","r",stdin);
        freopen("chain.out","w",stdout);
        n=read(9); 
        for(int i=2;i<=n;++i) e[f[i]=read(9)].emplace_back(i);
        caterpillar::predfs(1), caterpillar::relabel(1,1);
        SgT::build(1,1,n);
        for(int q=read(9); q; --q) {
            int opt=read(9), x=read(9);
            if(opt==1) Modify(x);
            else if(opt==2) print(handleChain(x),'\n');
            else print(handleSubtree(x),'\n');
        }
        return 0;
    }
    

    \(\cal T_3\) 基础树论练习题 / tree

    Description

    定义一棵有标号有根树合法当且仅当每一个点的儿子个数属于 \(S\),依次生成 $ k $ 个 \(n\) 个点有标号有根树森林。
    \(\forall\ 0 \leqslant i<n\),求出有多少种不同的生成方案使得其中恰有 \(ik\) 棵合法的树。输出结果模 \(p\) 后的值。

    \[\begin{array}{|c|c|c|c|c|}\hline 子任务编号 & p & k & 子任务依赖 & 分数 \\\hline 1 & & =1 & & 10 \\\hline 2 & & \leqslant 10^{4} & 1 & 10 \\\hline 3 & \leqslant 100 & & & 30 \\\hline 4 & \leqslant 10^{4} & & 3 & 20 \\\hline 5 & & & 2,4 & 30 \\\hline\end{array} \]

    \(1\leqslant n\leqslant 20,n<p\leqslant 10^5+3,1\leqslant k\leqslant 10^{16}\),且 \(p\) 是质数。

    Solution

    一些闲话:这题在考场上是真的做不动啊……

    \(k=1\)

    实际上就是求 \(n\) 个点的森林,含 \(i,∀\ 0 \leqslant i < n\) 个合法的树的方案数。但是我这也不会大雾

    \(f_i\) 为节点个数为 \(i\) 的合法树,\(F(x)\)\(f_i\)\(\mathtt{EGF}\)。事实上 "合法的树" 是很难直接描绘的,所以考虑递推地求解 —— 用 \(F(x)\) 来描绘 \(F(x)\).

    具体地,我们可以钦定一个根,再枚举它的子树,就有

    \[F(x)=x\cdot\left(\sum_{i\in S}\frac{F^i(x)}{i!}\right) \]

    之所以选用 \(\mathtt{EGF}\) 也是看在其重标号的功能,那个 \(1/i!\) 是为了去除卷积时子树的互异。由于 \(n\leqslant 20\),所以这个柿子大概是可以硬算的 qwq.

    那么对于 \(i\),所求即为

    \[n![x^n]\frac{F^i(x)}{i!}\cdot \exp(G(x)-F(x)) \]

    它的意义就是枚举 \(i\) 棵合法的树,其余部分则是 不合法的森林\(G(x)\) 是有标号有根树的 \(\mathtt{EGF}\).

    \(k\leqslant 10^4\)

    可以把上面的答案式子写成 \(\mathtt{OGF}\) 的形式(记其为 \(H(x)\)),那么实际上是求 \(H^k(x)\)(最多需要求到大约 \(x^{nk}\) 项),多项式求幂可以做到 \(\mathcal O(nk\log (nk))\).

    后面的内容大约学不会了,学会了大约也用不来,所以就鸽了 。

    Code

    啥也没有。
    
  • 相关阅读:
    推箱子
    为textarea增加maxlength属性(转)
    validate
    keypress
    Knockout
    &amp; replace &
    银联参数
    chinapay
    model binding
    JSON.stringify
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/16440238.html
Copyright © 2020-2023  润新知