zoukankan      html  css  js  c++  java
  • ARC104游记

    ARC104游记

    本人打的第一场 ARC ,还好没有太难看。

    A Plus Minus

    题意简述

    给定 (X+Y)(X-Y) ,求 (X)(Y)

    题目分析

    (X=((X+Y)+(X-Y))/2,Y=((X+Y)-(X-Y))/2)

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    int main(){
    	int a,b;
    	read(a),read(b);
    	write((a+b)/2),pc(' '),write((a-b)/2);
    	return 0;
    }
    
    

    B DNA Sequence

    题意简述

    给定碱基序列 (S) ,询问 (S) 中有多少个子串 (T) 满足 (T) 重排后可以与重排前配对。

    (1le |S|le 5000)

    题目分析

    (T) 重排后可以与重排前配对当前仅当 (T)A 的数量等于 T 的数量、 C 的数量等于 G 的数量,枚举所有子串,然后前缀和差分或者动态维护求出 A T C G 数量,直接判断即可。

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    const int maxn=5005;
    char s[maxn];
    int A[maxn],C[maxn],G[maxn],T[maxn];
    int main(){
    	int n;read(n);
    	scanf("%s",s+1);
    	for(int i=1;i<=n;++i){
    		A[i]=A[i-1]+(s[i]=='A');
    		C[i]=C[i-1]+(s[i]=='C');
    		G[i]=G[i-1]+(s[i]=='G');
    		T[i]=T[i-1]+(s[i]=='T');
    	}
    	int ans=0;
    	for(int i=1;i<=n;++i){
    		for(int j=i;j<=n;++j){
    			if(A[j]-A[i-1]==T[j]-T[i-1]&&C[j]-C[i-1]==G[j]-G[i-1])
    				++ans;
    		}
    	}
    	write(ans),pc('
    ');
    	return 0;
    }
    
    

    C Fair Elevator

    题意简述

    有一个长度为 (2N) 的序列 (1,2,dots,2N) ,有 (N) 个配对关系 (A_i o B_i(A_i<B_i)) ,满足每个 (1sim 2N) 中的每个数只出现一次,并且如果 (l_0,r_0) 配对且 (l_1,r_1) 配对且 (l_0<l_1<r_0) 那么必须满足 (r_0-l_0=r_1-l_1) ,现在有些关系损坏了(即有一些 (A_i)(B_i) 将不会给出),请问给出的配对关系是否可能合法(满足上述条件)。

    (1le Nle 100)

    题目分析

    假设存在配对关系 (l o r(l<r)) ,并且没有配对关系跨越 (l) ,那么必然存在配对关系 (l+1 o r+1,l+2 o r+2,dots,r-1 o r+r-l-1) ,也就是说这些配对关系构成了一个区间 ([l,r+r-l-1]) ,由此可以得到启发:或许我们可以使用区间 dp 来解决这个问题。

    (dp(l,r)) 表示仅考虑 ([l,r]) 这个区间内的数两两之间的配对,是否可以使得这个区间内的数互相两两配对。

    两种情况 (dp(l,r)) 是 true ,第一种情况就是 ([l,r]) 中的配对关系恰好构成了这个区间,第二种情况就是这个区间可以通过某个分界点分成两个区间,而这两个区间都是 true 。

    第一种情况直接暴力判断即可,第二种情况直接暴力枚举递归即可,时间复杂度 (mathcal O(n^3)) ,细节可能有点多,注意特判(我居然在考场上一遍 A ,真的舒适)。

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    const int maxn=105;
    int match[maxn*2],n;
    int Match(int d,int u){
    	int re=true;
    	if(match[d]){
    		if(match[d]<=n*2)
    			re&=(match[d]==u);
    		else
    			re&=(match[d]==n*2+1);
    	}
    	if(match[u]){
    		if(match[u]<=n*2)
    			re&=(match[u]==d);
    		else
    			re&=(match[u]==n*2+2);
    	}
    	if(match[d]==n*2+1&&match[u]==n*2+2)
    		re=false;
    	return re;
    }
    // 0:no match 1-n*2:ma n*2+1:up n*2+2:down
    int dp[maxn][maxn];
    int solve(int l,int r){
    	if(~dp[l][r])return dp[l][r];
    	int len=r-l+1,re=true;
    	for(int i=l;i<=r&&re;++i)
    		re&=Match(l-1+i,l-1+i+len);
    	if(re)return dp[l][r]=true;
    	for(int x=l;x<r&&!re;++x)
    		re|=solve(l,x)&&solve(x+1,r);
    	return dp[l][r]=re;
    }
    int main(){
    	int ok=true;read(n);
    	for(int i=1;i<=n&&ok;++i){
    		int a,b;
    		read(a),read(b);
    		if(a!=-1&&b!=-1&&a>=b)ok=false;
    		else{
    			if(a==-1&&b==-1)continue;
    			else if(a==-1){
    				if(match[b])ok=false;
    				else match[b]=n*2+2;
    			}
    			else if(b==-1){
    				if(match[a])ok=false;
    				else match[a]=n*2+1;
    			}
    			else{
    				if(match[a]||match[b])ok=false;
    				else match[a]=b,match[b]=a;
    			}
    		}
    	}
    	memset(dp,-1,sizeof dp);
    	if(!ok)puts("No");
    	else puts(solve(1,n)?"Yes":"No");
    	return 0;
    }
    
    

    D Multiset Mean

    题意简述

    给定 (N,K,M) ,对于每个整数 (1le xle N) ,求出构造整数序列 ({a}) ,满足 (a) 的长度为 (N) ,并且对于任意的 (1le ile N) ,都满足 (0le a_ile K) ,且 (frac{sum_{i=1}^na_i imes i}{sum_{i=1}^na_i}=x) (带权平均数为 (x) )对 (M) 取模的方案数。

    (1le N,Kle 100,10^8le Mle 10^9+9) ,保证 (M) 为质数。

    题目分析

    平均数为 (x) 不太好算,考虑到:

    [frac{sum_{i=1}^na_i imes i}{sum_{i=1}^na_i}=x ]

    [Leftrightarrow sum_{i=1}^na_i imes (i-x)=0 ]

    [Leftrightarrow sum_{i=1}^{n-x}a_i imes i=sum_{i=1}^{x-1}a_{x-i} imes i ]

    (f(n,m)) 为构造长度为 (n) 的序列 ({a},0le a_ile K) 满足 (m=sum_{i=1}^na_i imes i) 的方案数,然后递推计算,需要用到类似前缀和优化的方法,预处理时间复杂度是 (mathcal O(N^3K)) 的。

    然后枚举 (x) ,直接枚举 (m) ,找出令上面等式左边右边均为 (m) 的方案数,需要注意一点细节,比如 (a_x) 的取值,这部分的时间复杂度是 (mathcal O(N^3K)) 的。

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<assert.h>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    const int maxn=105,maxs=510000;
    int m;
    int mo(const int x){
    	return x>=m?x-m:x;
    }
    int k;
    int dp[maxn][maxs];
    int main(){
    	int n;
    	read(n),read(k),read(m);
    	dp[0][0]=1;
    	for(int i=1,mx=0;i<=n;++i){
    		mx+=i*k;
    		dp[i][0]=1;
    		for(int j=0;j<i;++j)
    			dp[i][j]=dp[i-1][j];
    		for(int j=i;j<=mx;++j){
    			dp[i][j]=mo(dp[i][j-i]+dp[i-1][j]);
    			if(j>=i*(k+1))dp[i][j]=mo(m-dp[i-1][j-i*(k+1)]+dp[i][j]);
    		}
    	}
    	for(int i=1;i<=n;++i){
    		if(i==1||i==n)write(k),pc('
    ');
    		else{
    			int l=i-1,r=n-i,ans=0;
    			int mx=min(l*(l+1)/2,r*(r+1)/2)*k;
    			for(int j=1;j<=mx;++j)
    				ans=mo(ans+1ll*dp[l][j]*dp[r][j]%m);
    			ans=1ll*ans*(k+1)%m;ans=mo(ans+k);
    			write(ans),pc('
    ');
    		}
    	}
    	return 0;
    }
    
    

    E Random LIS

    这题考场上没切,感觉这种 dp 方法以前都没有用过,所以有点不太会用。

    题目简述

    给定长度为 (N) 的序列 ({A}) ,有一个整数序列 ({a}) ,其中 (a_i)([1,A_i]) 中均匀随机,求 (a_i) 最长严格递增子序列的期望长度,模数 (10^9+7)

    (1le Nle 6,1le A_ile 10^9)

    题目分析

    这个 (N) 怎么这么小啊?这个 (A) 怎么这么大啊?

    考虑直接枚举所有可能的大小关系,也就是枚举有顺序集合划分 (S_1,S_2,dots,S_k) ,满足 (S_i) 中的 (a) 两两相等,并且 (S_i) 中的 (a) 小于 (S_{i+1}) 中的 (a) (当 (N=6) 时,这个枚举次数为 (4683) ),然后求方案数,因为 (S) 都被枚举出来了,所以这个方案数对答案的贡献也就求出来了,接下来的问题就是求方案数了。

    求方案数的时候把所有相等的 (a) 合并,其中的 (A_i) 去最小值,问题就转化成了:给定长度为 (N) 的序列 ({A}) ,有一个整数序列 ({a}) ,其中 (a_i)([1,A_i]) 中均匀随机,求 (a_i) 是严格递增序列的方案数。

    然后就变成了这道题目了:CF1295F;这道题目也有点像:[APIO2016]划艇

    在这里简单讲一下做法,值域这么大,显然要离散化,不妨规定离散化后的第 (i) 小的数和第 (i+1) 小的数组成的区间为第 (i) 个区间,离散化完后设 (f_{i,j}) 表示仅考虑序列的前 (i) 项,其中 (a_i) 的取值在离散化后的第 (j) 个区间内的方案数,转移就是枚举 (a_i) 前面有多少个也在第 (j) 个区间内。

    具体转移如下(设第 (j) 个区间为 (I_j) ,区间长度为 (L_j)(a_k) 的取值区间为 (S_k) ):

    [f_{i,j}=sum_{k=0}^{j-1}f_{i-1,k}{L_jchoose j-k}[forall k<tle i,I_jin S_t] ]

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    const int maxn=10,mod=1000000007;
    int mo(const int x){
    	return x>=mod?x-mod:x;
    }
    int power(int a,int x){
    	int re=1;
    	while(x){
    		if(x&1)re=1ll*re*a%mod;
    		a=1ll*a*a%mod,x>>=1;
    	}
    	return re;
    }
    int inv[maxn],st[maxn],cp[maxn],tp,all[maxn],f[maxn],g[maxn];
    int solve(void){
    	int cn=0;
    	for(int i=1;i<=tp;++i)
    		st[i]=cp[i];
    	for(int i=1;i<=tp;++i)
    		all[cn++]=++st[i],f[i]=0;
    	all[cn++]=1;sort(all,all+cn);
    	cn=unique(all,all+cn)-all;
    	for(int i=1;i<=tp;++i)
    		st[i]=lower_bound(all,all+cn,st[i])-all;
    	f[0]=1;
    	for(int i=0;i<cn-1;++i){
    		int len=all[i+1]-all[i];g[0]=1;
    		for(int j=1;j<=tp;++j)g[j]=1ll*g[j-1]*inv[j]%mod*(len-j+1)%mod;
    		for(int j=tp;j;--j)if(i<st[j]){
    			for(int k=j-1;~k;--k){
    				f[j]=mo(f[j]+1ll*f[k]*g[j-k]%mod);
    				if(i>=st[k])break;
    			}
    		}
    	}
    	return f[tp];
    }
    int a[maxn],id[maxn],n,ans,pos[maxn],mx[maxn];
    void dfs(int no){
    	if(no==0){
    		int cnt=0;
    		for(int i=n;i>=1;--i){
    			mx[i]=1;
    			for(int j=i+1;j<=n;++j){
    				if(pos[i]==pos[j]||id[j]>=id[i])continue;
    				mx[i]=max(mx[i],mx[j]+1);
    			}
    			cnt=max(cnt,mx[i]);
    		}
    		return ans=mo(ans+1ll*cnt*solve()%mod),void();
    	}
    	int U=1<<no;++tp;
    	for(int s=1;s<U;++s){
    		cp[tp]=1000000001;
    		int up=no;
    		for(int i=no;i>=1;--i){
    			if((s>>(i-1))&1){
    				cp[tp]=min(cp[tp],a[id[i]]);
    				pos[up]=tp;swap(id[i],id[up--]);
    			}
    		}
    		dfs(up);
    		for(int i=1;i<=no;++i){
    			if((s>>(i-1))&1){
    				swap(id[i],id[++up]);
    			}
    		}
    	}
    	--tp;
    }
    int main(){
    	read(n);int sum=1;
    	for(int i=1;i<=n;++i)
    		read(a[i]),id[i]=i,inv[i]=(i==1?1:1ll*(mod-mod/i)*inv[mod%i]%mod),sum=1ll*sum*a[i]%mod;
    	dfs(n);write(1ll*ans*power(sum,mod-2)%mod);pc('
    ');
    	return 0;
    }
    
    

    F Visibility Sequence

    这题考场上也没有 A ,不过这题感觉还是比 E 要好想的,可惜我先做的 E ,然后就几乎没有想 F 了。

    题意简述

    对于一个长度为 (N) 的序列 ({H}) ,定义 ({H}) 的“特殊序列”为长度也为 (N) 的序列 ({P}) ,满足 (P_i=max{{jmid j<i&& H_j>H_i}cup{-1}})

    现在给定一个长度为 (N) 的序列 ({X}) ,请问对于所有满足 (1le H_ile X_i) 的序列 ({H}) ,有多少个不同的“特殊序列”。

    (1le Nle 100,1le X_ile 10^5)

    题目分析

    说句闲话,这题 (X_i) 也可以开到 (10^9)

    方便起见,不妨认为 (H_0=infty) ,这样的话对于 (1sim N) 的每一个数, (P) 都不为 (-1)

    考虑在 (i)(P_i) 中连一条边,那么我们必然会得到一棵树,而这棵树的根就是 (0) ,考虑 dfs 这棵树,显然我们能够找到一种方法使得 dfs 序从 (1)(N) 递增,这个比较好理解,画张图就知道了。

    一棵子树管辖的 dfn 序必然是一个区间,这也对应着原序列的一个区间,所以可以考虑使用区间 dp 来做,但是问题是, (X_i) 如何处理。

    注意到这里的 (P_i) 我们求的是本质不同的个数,也就是说,对于相同的 (P_i) ,我们只需要找到一个满足特殊序列为 (P) 的构造方案即可,由于 (X_i) 的限制,所以我们要让这个构造方案给每个 (i) 分配的 (H_i) 尽可能小,这样就可以尽可能多地构造方案了。

    如何分配尽可能小的 (H) ?对于某个节点 (u) 和它所有的儿子节点 ({v}) 和在它左边的第一个兄弟节点 (w)(H_ugemax(max{H_v}+1,H_w)) ,因为只有这样,才满足我们给它构造的树形结构,那么,我们不妨认为一个构造方案是合法的,当且仅当对于任意的 (u) ,都满足 (H_u=max(max{H_v}+1,H_w)) ,不难发现,合法的构造方案中的 (H_i) 最大为 (N+1) ,并且每一种合法的构造方案都对应着一种方案,并且没有重复。

    由此我们可以得知,我们只用统计合法的构造方案即可,这样可以做到不重不漏,并且可以判断是否满足 (H_ile X_i)

    如何统计?设 (f_{l,r,x}) 表示 ([l,r]) 作为一棵树,根为 (l) ,并且 (H_l=x) 的合法方案数,那么我们可以枚举断点,然后给 (l) 加一棵子树,由于增加的子树导致了 (H_l) 的改变:

    [f_{l,r,x}gets f_{l,r,x}+sum_{i=l+1}^r(sum_{y=1}^x(f_{l,i-1,y}) imes f_{i,r,x-1}) ]

    还有一种情况,就是由于兄弟的 (H) 导致了添加子树的 (H) 增大:

    [f_{l,r,x}gets f_{l,r,x}+sum_{i=l+1}^r(f_{l,i-1,x} imessum_{y=1}^{x-2}(f_{i,r,y}) imes [X_ige x-1]) ]

    注意一下后面的 ([X_ige x-1]) ,因为要增大的前提是能够增大。

    发现有 (sum_{y=1}^xf_{l,i-1,y}) ,所以直接前缀和优化。

    时间复杂度: (mathcal O(N^4))

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    const int maxn=105,mod=1000000007;
    int mo(const int x){
    	return x>=mod?x-mod:x;
    }
    int f[maxn][maxn][maxn],g[maxn][maxn][maxn],a[maxn];
    int main(){
    	int n;read(n);a[1]=++n;
    	for(int i=2;i<=n;++i)read(a[i]);
    	for(int i=1;i<=n;++i){
    		f[i][i][1]=1;
    		for(int j=1;j<=n;++j)
    			g[i][i][j]=mo(g[i][i][j-1]+f[i][i][j]);
    	}
    	for(int len=1;len<n;++len){
    		for(int l=1,r=l+len;r<=n;++l,++r){
    			for(int x=2;x<=n&&x<=a[l];++x){
    				for(int i=l+1;i<=r;++i){
    					f[l][r][x]=mo(f[l][r][x]+1ll*g[l][i-1][x]*f[i][r][x-1]%mod);
    					f[l][r][x]=mo(f[l][r][x]+1ll*f[l][i-1][x]*g[i][r][x-2]*(a[i]>=x-1)%mod);
    				}
    			}
    			for(int x=1;x<=n;++x)
    				g[l][r][x]=mo(g[l][r][x-1]+f[l][r][x]);
    		}
    	}
    	write(g[1][n][n]),pc('
    ');
    	return 0;
    }
    
    

    总结

    自己考试的时候还是太急躁了,一直都在想 E ,后面发现了类似的题目也是做不出来,只能干着急,就没有去想 F 了,真是一大失误。

    不过总体成绩还是很好的。

  • 相关阅读:
    AGC037F Counting of Subarrays
    AGC025F Addition and Andition
    CF506C Mr. Kitayuta vs. Bamboos
    AGC032D Rotation Sort
    ARC101F Robots and Exits
    AGC032E Modulo Pairing
    CF559E Gerald and Path
    CF685C Optimal Point
    聊聊Mysql索引和redis跳表
    什么是线程安全
  • 原文地址:https://www.cnblogs.com/lsq147/p/13768620.html
Copyright © 2011-2022 走看看