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 了,真是一大失误。

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

  • 相关阅读:
    在ORACLE里用存储进程活期瓜分表
    TSM Server,Client,TDPO安装设置装备摆设手记
    用Linux完成Oracle自植物理备份
    在Red Hat Linux7.x/8.0下安设Oracle 9i
    运用Oracle9i数据库的细心事变
    科来网络分析系统 V6.0
    [恢]hdu 2034
    [恢]hdu 2041
    [恢]hdu 2015
    [恢]hdu 2035
  • 原文地址:https://www.cnblogs.com/lsq147/p/13768620.html
Copyright © 2011-2022 走看看