• Deltix Round, Spring 2021 (open for everyone, rated, Div. 1 + Div. 2)


    (sf E-Crypto Lights)

    题目

    给定长度为 (n) 的初始均为 (0) 的序列,你有如下操作:

    1. 值为 (0) 的点之间等概率选择一个点变成 (1)
    2. 如果有两个值为 (1) 的点距离 (<k),结束操作。否则回到 1.

    你需要求出最终序列 (1) 个数的期望。

    解法

    首先,“最终序列 (1) 个数的期望” 等价于 “结束时操作次数期望”。

    这样不太好算,因为 “结束时” 和 “结束前” 两个状态不太一样。于是我们可以再进行一步转化:结束前操作次数期望 (+1)

    钦定结束前操作次数为 (i),那么到达每个操作次数为 (i) 状态的概率是 (frac{1}{n imes (n-1) imes... imes (n-i+1)}) 也即 (frac{(n-i)!}{n!})

    那么如何计算合法的状态呢?考虑如果没有 (k) 的限制,合法种类数就是 ( ext{C}_{n}^i imes i!)。有一个奇妙的发现,就是合法状态每两个 (1) 之间必定有至少 (k-1)(0)!那么类似小球入盒的解决方案,我们把每两个 (1) 之间一定有的 (k-1)(0) 先抛掉,那么就剩下 (n-(k-1)(i-1)) 个位置,我们在里面任选 (i) 个位置,再在每两个 (1) 之间插入 (k-1)(0)。容易发现这是等价于原问题的,因为一段 (0) 的相对位置对方案没有影响。

    所以有:

    [ ext{Ans}=1+sum_{i=1}^n ext{C}_{n-(k-1)(i-1)}^i imes i! imes frac{(n-i)!}{n!} ]


    (sf G - Try Booking)

    解法

    既然要求互不相交,你会发现长度 (x) 最多只能选 (frac{n}{x}) 个区间。所以惊喜地发现总区间个数是 (sum_{x=1}^n frac{n}{x}approx nln n)

    对于每个 (x),如果我们找到编号最小的区间 ([l,r]),那么接下来需要在 ([1,l-1]cup [r+1,n]) 中寻找编号最小的区间… 以此类推。

    问题转化为在查询区间中找编号最小的区间。不过这不能用线段树维护,因为线段树可能会出现查询区间与小区间相交的情况。

    我想用 (mathtt{cdq}) 分治!但是注意到此题的询问区间是动态的,必须询问到上一个编号最小的区间才能确定下一个询问。

    那就… 树套树!

    代码

    还是放一个吧…


    (sf H - Hopping Around the Array)

    解法

    首先发现设计关于 (i ightarrow j) 的最小步数的状态是不现实的,它的范围高达 (4e8)。既然询问只问 ((l_i,r_i,k_i)),为什么不试试类似分块的做法,一块块地拼凑出答案?

    事实上,这个问题可以用倍增巧妙地解决。

    (dp_{k,i,j}) 表示删除不超过 (k) 个点,从 (i) 开始走 (2^j) 步能到达的最远位置。

    由于 (k) 比较小,我们可以枚举 (k_1,k_2) 来进行转移:

    [dp_{k_1+k_2,i,j}=max_{p=i}^{dp_{k_1,i,j-1}}dp_{k_2,p,j-1} ]

    具体可以用 (mathtt{st}) 表来实现。

    如何统计答案?想想我们是如何用倍增求解 ( m lca) 的,这里也类似:从大到小枚举 (2^j) 步,用 (mathtt{st}) 表维护对应步数 (dp) 值的区间 (max)。对于每个询问,定义 (f_k) 为删除不超过 (k) 个点从 (l_i) 能走到的最远的小于 (r_i) 的位置,每枚举一个步数就用类似于上文的转移来转移 (f) 值,如果此时 (f_{k_i}<r_i) 就说明可以走这个步数,否则就将 (f) 回退到原来的状态。

    最后将每个询问的步数 (+1) 就是答案。

    整体时间复杂度约为 (mathcal O(900 imes nlog n))

    代码

    下次高维 (mathtt{dp}) 再不按循环顺序来我就是 (mathtt{sb})

    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC target("avx")
    #pragma GCC optimize("Ofast")
    #include <cstdio>
    
    #define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
    #define fep(i,_l,_r) for(signed i=(_l),_end=(_r);i>=_end;--i)
    #define print(x,y) write(x),putchar(y)
    
    template <class T> inline T read(const T sample) {
        T x=0; int f=1; char s;
        while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
        while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
        return x*f;
    }
    template <class T> inline void write(const T x) {
        if(x<0) return (void) (putchar('-'),write(-x));
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
    template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
    
    const int maxn=2e4+4;
    
    int n,q,lg[maxn],a[maxn],st[32][18][maxn],dp[18][32][maxn],ans[maxn],f[maxn][32],tmp[32];
    struct node {
    	int l,r,k;
    } s[maxn];
    
    void init() {
    	rep(i,2,n) lg[i]=lg[i>>1]+1;
    }
    
    int Query(int k,int l,int r) {
    	int d=lg[r-l+1];
    	return Max(st[k][d][l],st[k][d][r-(1<<d)+1]);
    }
    
    int main() {
    	n=read(9),q=read(9); init();
    	int lgn=lg[n]+1;
    	rep(i,1,n) a[i]=read(9);
    	rep(k,0,30) rep(i,1,n) dp[0][k][i]=Min(n,i+a[i]+k);
    	rep(j,1,lgn) {
    		rep(k,0,30) rep(i,1,n) st[k][0][i]=dp[j-1][k][i];
    		rep(k,0,30) rep(i,1,lgn) rep(p,1,n)
    			if(p+(1<<i)-1<=n)
    				st[k][i][p]=Max(st[k][i-1][p],st[k][i-1][p+(1<<i-1)]);
    			else break;
    		rep(k1,0,30) rep(k2,0,30) {
    			if(k1+k2>30) break;
    			rep(i,1,n) dp[j][k1+k2][i]=Max(dp[j][k1+k2][i],Query(k1,i,dp[j-1][k2][i]));
    		}
    	}
    	rep(i,1,q) {
    		s[i].l=read(9),s[i].r=read(9),s[i].k=read(9);
    		if(s[i].l==s[i].r) ans[i]=-1;
    		else rep(j,0,s[i].k) f[i][j]=s[i].l;
    	}
    	fep(j,lgn,0) {
    		rep(k,0,30) rep(i,1,n) st[k][0][i]=dp[j][k][i];
    		rep(k,0,30) rep(i,1,lgn) rep(p,1,n)
    			if(p+(1<<i)-1<=n)
    				st[k][i][p]=Max(st[k][i-1][p],st[k][i-1][p+(1<<i-1)]);
    			else break;
    		rep(i,1,q) {
    			if(ans[i]==-1) continue;
    			rep(k,0,s[i].k) tmp[k]=0;
    			rep(k1,0,s[i].k) rep(k2,0,s[i].k) {
    				if(k1+k2>s[i].k) break;
    				tmp[k1+k2]=Max(tmp[k1+k2],Query(k1,s[i].l,f[i][k2]));
    			}
    			if(tmp[s[i].k]<s[i].r) {
    				ans[i]+=(1<<j);
    				rep(k,0,s[i].k) f[i][k]=tmp[k];
    			}
    		}
    	}
    	rep(i,1,q) print(ans[i]+1,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    线程高并发
    29(套接字)就是网络编程
    28线程
    27 枚举
    26静态导入和可变参数
    25JDK新特性
    25断言 assert关键字
    24单元测试 junit
    炫酷CSS
    PHP 汉字转拼音类
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/14842358.html
Copyright © 2020-2023  润新知