Codeforces 1111簡単な問題解


文書ディレクトリ
  • A題
  • B題
  • C題
  • D題
  • E題
  • トランスファゲート
    A題
    トランスポートゲートの問題の簡単な説明:26の26個の英文の字母を2種類のAに分けて、B A、B A、B、A A類の文字はA A類の文字に回転することができて、B類の文字はB類の文字に回転することができて、与えた2つの文字列のS S S SはT T Tに変換することができますかを聞きます
    考え方:まず長さを判断して、それからS、T S、Tの中でA A類をa a aに変えて、B B類をb bに変えて、それから違いを見ます.コード:
    #include
    #define ri register int
    using namespace std;
    const int N=1005;
    int n,m;
    char s[N],t[N];
    const char ch[5]={'a','e','i','o','u'};
    int main(){
    	scanf("%s%s",s+1,t+1);
    	n=strlen(s+1),m=strlen(t+1);
    	if(n!=m)return puts("No"),0;
    	for(ri i=1;i<=n;++i){
    		bool f=0;
    		for(ri j=0;j<5;++j)if(s[i]==ch[j]){f=1;break;}
    		if(f==1)s[i]='a';
    		else s[i]='b';
    	}
    	for(ri i=1;i<=n;++i){
    		bool f=0;
    		for(ri j=0;j<5;++j)if(t[i]==ch[j]){f=1;break;}
    		if(f==1)t[i]='a';
    		else t[i]='b';
    		if(s[i]!=t[i])return puts("No"),0;
    	}
    	puts("Yes");
    	return 0;
    }
    

    B題
    転送ゲートの問題の簡単な説明:n nの数をあげて、あなたは多くのm m m回まで操作することができて、毎回1つの数に1 1 1をプラスするか、あるいは1つの数を削除して、最後に残った数の最大平均値を聞くことができます.
    考え方:すべての数を並べ替えて、前のi i iの数を列挙して答えを更新すればいい.
    コード:
    #include
    #define ri register int
    using namespace std;
    inline int read(){
    	int ans=0;
    	char ch=getchar();
    	while(!isdigit(ch))ch=getchar();
    	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    	return ans;
    }
    typedef long long ll;
    const int N=1e5+5;
    ll a[N];
    ll sum[N];
    int n,k,m;
    int main(){
    	n=read(),k=read(),m=read();
    	for(ri i=1;i<=n;++i)a[i]=read();
    	sort(a+1,a+n+1);
    	for(ri i=1;i<=n;++i)sum[i]=sum[i-1]+a[i];
    	double ans=0.0;
    	for(ri i=1;i<=min(n,m+1);++i){
    		ll up=min((ll)m-i+1,(ll)(n-i+1)*k);
    		ans=max(ans,1.0*(sum[n]-sum[i-1]+up)/(n-i+1));
    	}
    	printf("%.10lf
    "
    ,ans); return 0; }

    C題
    トランスポートゲートの問題は簡単に述べる:1つの2 n 2^n 2 nの長いシーケンスに、n≦30 nle 30 n≦30を与えて、その中にk k個の特殊な点k≦100000 kle 100000 k≦100000があって、今シーケンス全体を除去することを要求して、現在のシーケンスに対していくつかの操作方式があります.
  • 現在のシーケンス等分をそれぞれ
  • 除去する.
  • 現在のシーケンスを消去し、現在のシーケンスに特別なポイントがない場合A Aを費やす.そうでない場合、B∗特殊ポイント数∗現在のシーケンス長B*特殊ポイント数*現在のシーケンス長B∗特殊ポイント数∗現在のシーケンス長
  • を費やす
    保証A,B≧0 A,Bge 0 A,B≧0はシーケンス全体の最小費用を除去する.
    考え方:現在の区間に特殊点が1つしか残っていない場合はB Bを直接返し、特殊点がなければA A Aを直接返し、そうでなければ題意通りにシミュレーションします.
    コード:
    #include
    #define ri register int
    using namespace std;
    typedef long long ll;
    inline ll read(){
    	ll ans=0;
    	char ch=getchar();
    	while(!isdigit(ch))ch=getchar();
    	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    	return ans;
    }
    const int N=1e6+5;
    ll n,k,m,a[N];
    ll A,B;
    inline ll cnt(ll l,ll r){return upper_bound(a+1,a+k+1,r)-lower_bound(a+1,a+k+1,l);}
    inline ll solve(ll l,ll r){
    	ll mid=l+r>>1,tmp=cnt(l,r);
    	if(!tmp)return A;
    	ll ret=B*(r-l+1)*tmp;
    	if(l==r)return ret;
    	return min(ret,solve(l,mid)+solve(mid+1,r));
    }
    int main(){
    	n=read(),k=read(),A=read(),B=read();
    	for(ri i=1;i<=k;++i)a[i]=read();
    	sort(a+1,a+k+1);
    	cout<<solve(1,1<<n);
    	return 0;
    }
    

    D題
    転送ドアのタイトルの簡単な説明:n,n≦1 e 5 n,nle 1 e 5 n,n≦1 e 5色(色は最大52,52,52種)の点(n n n nは偶数)を任意に配列することができ、合法的な配列は各色について、すべての色の点が前半または同後半にあることを規定し、現在q q qはq≦100000 qle 100000 q≦100000 q,2つの色x,y x,y x,yを指定するたびに、この2つの色に対応するすべての点が前半または後半にあることを要求し、この条件の下で合法的なシーケンスの個数を尋ねる.
    考え方:色ごとの個数を貯金して、01 01リュックサックを作ることを考えて、f i f_i fiは、i i iを絞り出すシナリオ数を表す.このように順序とx,y x,y x,yの制限f n 2 f_を考慮しない{frac n 2}f 2 nはすべての合法的なシーケンス数であり、その後、x、y x、y x、yのペアごとにそれらの寄与をa n s x、y ans_として列挙する.{x,y}ansx,yは終わりです.最後に順序の問題を考慮すると,合法的なシーケンス数に対して明らかに対応できる(n 2!)2 ∗ ∏ i = 0 , 52 i n v f a c c n t i (\frac n2!)^2*\prod_{i=0,52}invfac_{cnt_i} (2n​!)2∗∏i=0,52 invfaccti中放法.
    コード:
    #include
    #define ri register int
    using namespace std;
    typedef long long ll;
    inline int read(){
    	int ans=0;
    	char ch=getchar();
    	while(!isdigit(ch))ch=getchar();
    	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    	return ans;
    }
    const int mod=1e9+7;
    inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
    inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
    inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
    const int N=1e5+5;
    char s[N];
    int tmp,n,q,cnt[52],fac[N],ifac[N],f[N],ans[52][52],tim;
    inline int idx(char x){return x>='a'&&x<='z'?x-'a':x-'A'+26;}
    int main(){
    	scanf("%s",s+1),n=strlen(s+1);
    	for(ri i=1;i<=n;++i)++cnt[idx(s[i])];
    	fac[1]=ifac[1]=fac[0]=ifac[0]=1;
    	for(ri i=2;i<=n;++i)fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[mod%i],mod-mod/i);
    	for(ri i=2;i<=n;++i)ifac[i]=mul(ifac[i],ifac[i-1]);
    	f[0]=1;
    	for(ri i=0;i<52;++i){
    		if(!cnt[i])continue;
    		for(ri j=n;j>=cnt[i];--j)f[j]=add(f[j],f[j-cnt[i]]);
    	}
    	tim=mul(fac[n/2],fac[n/2]);
    	for(ri i=0;i<52;++i)tim=mul(tim,ifac[cnt[i]]);
    	for(ri i=0;i<52;++i){
    		if(!cnt[i])continue;
    		ans[i][i]=f[n/2];
    		for(ri j=cnt[i];j<=n;++j)f[j]=dec(f[j],f[j-cnt[i]]);
    		for(ri j=0;j<i;++j){
    			if(!cnt[j])continue;
    			for(ri k=cnt[j];k<=n;++k)f[k]=dec(f[k],f[k-cnt[j]]);
    			ans[i][j]=ans[j][i]=mul(2,f[n/2]);
    			for(ri k=n;k>=cnt[j];--k)f[k]=add(f[k],f[k-cnt[j]]);
    		}
    		for(ri j=n;j>=cnt[i];--j)f[j]=add(f[j],f[j-cnt[i]]);
    	}
    	for(ri x,y,i=read();i;--i){
    		x=idx(s[read()]),y=idx(s[read()]);
    		cout<<mul(ans[x][y],tim)<<'
    '
    ; } return 0; }

    E題
    転送ドアのテーマは簡単に説明します:あなたに1本の木をあげて、毎回あなたにいくつかの肝心な点と1つの根と1つのmをあげて、あなたにこれらの点を多くのmグループに分けて各グループの任意の2つの数を満たすように要求して祖先の関係がなくて、方案の数を求めます.キー総数≦100000,n≦100000,m≦m i n{n,300}le 100000,nle 100000,mle min{n,300}≦100000,n≦100000,m≦min{n,300}を満たす
    構想:1 1がルートである場合を直接考慮し、現在のすべてのキーを抽出して1つの集合を構成し、1つのf i,j f_を記す.{i,j}fi,jは集合中の前i i i個の数をj j j個のグループに分けてパケット方式で題意を満たすシナリオ数,g i g_を表す.i giは集合中のi i iの祖先の個数を表し,明らかにf i,j=f i−1,j−1+f i−1,j∗(j−g i)f_{i,j}=f_{i-1,j-1}+f_{i−1,j}*(j−g_i)fi,j=fi−1,j−1+fi−1,j∗(j−gi)というように,g g配列を毎回考慮するだけでよい.すべての点のd f s dfs dfsシーケンスを作成し、ツリー配列でg g gを維持し、ルートをl c a lca lcaで差分すればよい.
    コード:
    #include
    #define ri register int
    using namespace std;
    typedef long long ll;
    inline int read(){
    	int ans=0;
    	char ch=getchar();
    	while(!isdigit(ch))ch=getchar();
    	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    	return ans;
    }
    const int mod=1e9+7;
    inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
    inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
    inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
    const int N=2e5+5;
    int n,q,dep[N],g[N],f[N],siz[N],hson[N],fa[N],top[N],in[N],out[N],a[N],tot=0;
    bool mk[N];
    vector<int>e[N];
    void dfs1(int p){
    	siz[p]=1;
    	for(ri i=0,v;i<e[p].size();++i){
    		if((v=e[p][i])==fa[p])continue;
    		fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
    		if(siz[v]>siz[hson[p]])hson[p]=v;
    	}
    }
    void dfs2(int p,int tp){
    	top[p]=tp,in[p]=++tot;
    	if(!hson[p]){out[p]=++tot;return;}
    	dfs2(hson[p],tp);
    	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa[p]&&v!=hson[p])dfs2(v,v);
    	out[p]=tot;
    }
    inline int lca(int x,int y){
    	while(top[x]^top[y]){
    		if(dep[top[x]]<dep[top[y]])swap(x,y);
    		x=fa[top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    namespace Bit{
    	int bit[N]={0};
    	inline int lowbit(const int&x){return x&-x;}
    	inline void update(const int&x,const int&v){for(ri i=x;i<=tot;i+=lowbit(i))bit[i]=add(bit[i],v);}
    	inline int query(const int&x){int ret=0;for(ri i=x;i;i-=lowbit(i))ret=add(ret,bit[i]);return ret;}
    }
    int main(){
    	n=read(),q=read();
    	for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
    	dfs1(1),dfs2(1,1);
    	while(q--){
    		int k=read(),m=read(),rt=read(),ans=0;
    		for(ri i=1;i<=k;++i)mk[a[i]=read()]=1,Bit::update(in[a[i]],1),Bit::update(out[a[i]]+1,-1);
    		for(ri i=1,t;i<=k;++i)t=lca(a[i],rt),g[i]=Bit::query(in[a[i]])+Bit::query(in[rt])-2*Bit::query(in[t])+mk[t]-1;
    		sort(g+1,g+k+1);
    		for(ri i=1;i<=k;++i)Bit::update(in[a[i]],-1),Bit::update(out[a[i]]+1,1),mk[a[i]]=0;
    		bool flag=g[k]>=m;
    		if(flag){puts("0");continue;}
    		for(ri i=0;i<=m;++i)f[i]=0;
    		f[0]=1;
    		for(ri i=1;i<=k;++i){
    			for(ri j=min(i,m);~j;--j){
    				if(j<=g[i])f[j]=0;
    				else f[j]=add(f[j-1],mul(f[j],j-g[i]));
    			}
    		}
    		for(ri i=1;i<=m;++i)ans=add(ans,f[i]);
    		cout<<ans<<'
    '
    ; } return 0; }