• Atcoder Grand Contest 011&012


    012E Camel and Oases

    题目描述

    点此看题

    解法

    考试时直接切了,不知道这题有什么难的,我都会做的题肯定是水题

    首先有一个问题转化:我们可以将原序列划分为 \(\log\) 个连续段,使得每一段的 \(\max(x_i-x_{i-1})\leq v_j\),其中 \(v_j=\frac{k}{2^j}\) 表示第 \(j\) 次跳跃后背包的容量(注意 \(v_j=0\) 也是合法的,但是只能存在一次)

    首先考虑全局存不存在合法方案,一个重要的观察是只要我们确定 \(v\)分配顺序,那么可以通过贪心来确定段的划分,就是按照这个顺序能取就取。解决顺序问题可以考虑状压 \(dp\),设 \(dp[s]\) 表示已使用的 \(v\) 集合为 \(s\) 的最远延伸距离。

    那么怎么确定单个位置的答案呢?一个简单的观察是:\(x_i-x_{i-1}>k\) 的最多只存在 \(\log k\) 对,要不然就全局无解。所以我们以 \(x_i-x_{i-1}>k\) 为断点,由于每个点为起点都会取遍段的位置之后再离开,段中每个位置的答案是一样的

    设段是 \([l,r]\),设 \(f(s),g(s)\) 分别表示前缀 \(/\) 后缀的最远延伸位置,那么可以枚举集合 \(s\),设补集是 \(t\),判断条件就是:

    \[f(s)\geq l-1\and g(t)\leq r+1 \]

    都可以暴力做,时间复杂度 \(O(n\log n)\)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int M = 1<<21;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,t,v[30],p[30],l[22][M],r[22][M],x[M],f[M],g[M];
    signed main()
    {
    	n=read();m=read();
    	for(int x=m;x;x/=2) v[k++]=x;v[k++]=0;
    	for(int i=1;i<=n;i++) x[i]=read();
    	for(int w=0;w<k;w++)
    	{
    		l[w][0]=l[w][1]=1;
    		r[w][n]=r[w][n+1]=n;
    		for(int i=2;i<=n;i++)
    		{
    			if(x[i]-x[i-1]<=v[w])
    				l[w][i]=l[w][i-1];
    			else l[w][i]=i;
    		}
    		for(int i=n-1;i>=1;i--)
    		{
    			if(x[i+1]-x[i]<=v[w])
    				r[w][i]=r[w][i+1];
    			else r[w][i]=i;
    		}
    	}
    	memset(g,0x3f,sizeof g);g[0]=n+1;
    	for(int s=0;s<(1<<k);s++)
    		for(int i=0;i<k;i++) if(!(s>>i&1))
    		{
    			f[s|(1<<i)]=max(f[s|(1<<i)],r[i][f[s]+1]);
    			g[s|(1<<i)]=min(g[s|(1<<i)],l[i][g[s]-1]);
    		}
    	for(int i=2;i<=n;i++)
    		if(x[i]-x[i-1]>m) p[++t]=i;
    	if(t>k)
    	{
    		for(int i=1;i<=n;i++) puts("Impossible");
    		return 0; 
    	}
    	for(int x=1,y;x<=n;x=y+1)
    	{
    		y=r[0][x];int fl=0,t=(1<<k)-1-1;
    		for(int s=0;s<(1<<k);s++) if(!(s&1))
    			fl|=(f[s]>=x-1 && g[s^t]<=y+1);
    		for(int i=x;i<=y;i++)
    			puts(fl?"Possible":"Impossible");
    	}
    }
    

    011D Half Reflector

    题目描述

    点此看题

    解法

    首先要知道单次操作之后会有什么样的变化,如果首位是 A 那么会立马弹开,如果首位是 B,那么经过它之后会变成 A,我们继续考虑后续的情况,关键的观察是球的上一个位置一定是 A

    • 下一个位置是 B,那么对于 AB 会变成 AA
    • 下一个位置是 A,那么对于 AA 会变成 BA

    使用归纳法,我们可以发现球的作用相当于给每个位置左移之后取反(在环的意义下,正好最后一个位置一定是 A

    那么就可以通过打整体标记实现 \(O(k)\) 模拟了。考试时我观察数据发现后缀会趋向 ABABAB... 这种类型,而且在 \(2n\) 次之内就会稳定下来,但是我们需要保留原来 \(k\) 的奇偶性,这样我们就可以在 \(O(n)\) 的时间内模拟了。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 200005; 
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,k,a[M];char s[M];
    signed main()
    {
    	n=read();k=read();scanf("%s",s+1);
    	for(int i=1;i<=n;i++) a[i]=s[i]-'A';
    	int t=0,p=1;k=min(k,2*n+(k&1));
    	while(k--)
    	{
    		if(t^a[p]) t^=1,p=p%n+1;
    		else a[p]^=1;
    	}
    	for(int i=p;i<=n;i++) putchar((a[i]^t)?'B':'A');
    	for(int i=1;i<p;i++) putchar((a[i]^t)?'B':'A');
    }
    

    012F Prefix Median

    题目描述

    点此看题

    解法

    直接按照生成方式去规划 \(b\) 序列是困难的,我们需要先寻找一些 \(b\) 序列的性质。我们可以感受到,\(b\) 序列的每个元素都有其大致的范围,并且 \(b\) 序列的变化是连续的,那么写出必要条件:

    • \(b_i\in\{a_1,a_2...a_{2n-1}\}\),显然的必要性。
    • \(a_i\leq b_i\leq a_{2n-i}\),容易发现不在这个范围内一定无法成为中位数。
    • 对于 \(i\)\(i+1\),不存在 \(j\leq i\) 使得 \(p_i<p_j<p_{i+1}\) 或者 \(p_i>p_j>p_{i+1}\),因为中位数只能移动一个位置,这里要加 \(j\leq i\) 的原因就是只考虑前面出现过的数(所以这部分 soulist 讲错了)

    那么用上面的必要条件来构造即可证明充分性。考虑根据 \(p_{i+1}\)\(p_i\) 的大小关系来构造,如果 \(p_i=p_{i+1}\) 那么添加剩下的最大值和最小值;如果 \(p_i>p_{i+1}\),并且如果 \(p_{i+1}\) 是第一次出现,那么添加 \(p_{i+1}\) 和最小值,否则添加最小值和次小值;如果 \(p_i<p_{i+1}\),并且如果 \(p_{i+1}\) 是第一次出现,那么添加 \(p_{i+1}\) 和最大值,否则添加最大值和次大值。这样构造一定有解。

    那么用这个等价条件去规划 \(b\) 序列即可,首先我们把 \(a_i\) 排序,观察到 \(b_n=a_n\),那么我们以 \(n\) 为起始点往回推。由于 \(b\) 取值范围的变化我们需要加入 \(a_{i}\)\(a_{2n-i}\),我们已知 \(p_{i+1}\) 现在要确定 \(p_i\),确定之后我们需要删除 \((p_i,p_{i+1})\) 中的所有数。

    \(x=p_i\),那么我们记录 \(x\) 左边有多少数,\(x\) 右边有多少数即可,所以设 \(dp[i][l][r]\) 表示考虑到 \(p_i\)\(x\) 左边有 \(l\) 个数,\(x\) 右边有 \(r\) 个数的方案数。转移枚举 \(x'\) 即可,注意相同的数加入的时候需要当成一个数。

    时间复杂度 \(O(n^4)\)

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int M = 105;
    const int MOD = 1e9+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans,a[M],dp[M][M][M];
    void add(int &x,int y) {x=(x+y)%MOD;}
    signed main()
    {
    	n=read();m=2*n;
    	for(int i=1;i<m;i++) a[i]=read();
    	sort(a+1,a+m);
    	dp[n][0][0]=1;
    	for(int i=n-1;i>=1;i--)
    	{
    		int x=a[i]!=a[i+1],y=a[m-i]!=a[m-i-1];
    		for(int l=0;l<m;l++) for(int r=0;r<m;r++)
    		{
    			int t=dp[i+1][l][r];if(!t) continue;
    			add(dp[i][l+x][r+y],t);
    			for(int d=0;d<l+x;d++)
    				add(dp[i][d][r+y+1],t);
    			for(int d=0;d<r+y;d++)
    				add(dp[i][l+x+1][d],t); 
    		}
    	}
    	for(int l=0;l<m;l++) for(int r=0;r<m;r++)
    		add(ans,dp[1][l][r]);
    	printf("%d\n",ans);
    }
    
  • 相关阅读:
    CPNtools协议建模安全分析--ML语言之颜色集定义(六)
    CPNtools协议建模安全分析---实例变迁标记(五)
    CPNtools协议建模安全分析---实例库所标记(四)
    CPNtools协议建模安全分析---实例(三)
    CPNtools协议建模安全分析---实例(二)
    工业协议安全分析中形式化粒度问题分析
    LTS1.3秘钥导出和身份验证计算过程
    CPNtools协议建模安全分析(一)
    TLS 1.3 中Pre_shared_key和key_share对应的两种密钥交换模式
    传统Dolev-Yao攻击模型和eCK强安全模型之间的辨析
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15923452.html
Copyright © 2020-2023  润新知