CodeForces Round #586(Div1+Div2)

28585 ワード

A. Cards


テストアドレスの問題は簡単に説明します:長さnの文字列を与えて、この文字列はいくつかのoneといくつかのzeroを組み合わせることができて、各文字は一回しか使えません.
コードの例:
#include
#include
using namespace std;
const int N = 1e5+10;
char str[N];
int nn;
void solve(){
	int n,z,e,r,o;
	n = z = e = r = o = 0;
	for(int i = 1;i <= nn;i++){
		if(str[i] == 'o') o++;
		else if(str[i] == 'z') z++;
		else if(str[i] == 'r') r++;
		else if(str[i] == 'e') e++;
		else n++;
	}
	int one,zero;
	one = min(n,min(o,e));
	n -= one, o -= one, e -= one;
	zero = min(o,min(e,min(z,r)));
	for(int i = 1;i <= one;i++) printf("1 ");
	for(int i = 1;i <= zero;i++) printf("0 ");
}
int main(){
	scanf("%d",&nn);
	scanf("%s",str+1);
	solve();
	return 0;
}

B. Multiplication Table


テストアドレスの問題は簡単に説明します:1つのn*n表を与えて、ここでM i,j=a i∗a j M_{i,j} = a_i * a_j Mi,j=ai∗aj,今悪党が序列aを投げて、同時にM_{i,i}が引かれてしまいましたので、残りの情報を利用してaシーケンスを求めてください.
解題の構想:ガウス消元問題ではなく、実は法則を探す問題だ.もし私たちがa 1 aを求めることができたら1 a 1では、最初の列に基づいてすべての答えを求めることができます.a 1 a 2=x a_に基づいて1a_2 = x a1​a2​=x , a 1 a 3 = y a_1a_3 = y a1​a3​=y , a 2 a 3 = z a_2a_3=z a 2 a 3=zでa 1 a_を求める1 a 1で、他のすべての結果を求めることができます.コードの例:
#include
#include
int n;
const int N = 1e3+10;
typedef __int64 ll;
ll mat[N][N],ans[N];
void solve(){
	ans[1] = mat[1][2]*mat[1][3]/mat[2][3];
	ans[1] = (ll)sqrt(ans[1]);
	for(int i = 2;i <= n;i++)
		ans[i] = mat[1][i]/ans[1];
	for(int i = 1;i <= n;i++) printf("%I64d ",ans[i]);
}
int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;i++) 
		for(int j = 1;j <= n;j++) scanf("%I64d",&mat[i][j]);
	solve();
	return 0;
}

C. Substring Game in the Lesson


AnnとMikeはゲームをしています.文字列sがあります.最初はl=r=kで、一人一人が交代で操作します.
  • l'
  • 何もできないと失敗します.

  • Ann先手は,位置kごとに,誰が勝つかを出力する.
    簡単なゲーム理論では、まず必敗状態は「lの前にl'コードの例:
    #include
    #include
    #include
    using namespace std;
    const int N = 5e5+10;
    const int INF = 0x3f3f3f3f;
    char str[N];
    int vis[N];
    void solve(){
    	int mi = (int)str[0],n = strlen(str);
    	for(int i = 1;i < n;i++){
    		if(mi < (int)str[i]) vis[i] = 1;
    		mi = min(mi,(int)str[i]);
    	}
    	for(int i = 0;i < n;i++)
    		if(vis[i]) printf("Ann
    "
    ); else puts("Mike"); } int main(){ scanf("%s",str); solve(); return 0; }

    D. Alex and Julian


    テストアドレスの題意は簡単に述べる:1つの正の整数の集合Bを与えて、全集Z(すべての整数)内の要素を無方向図の頂点として、図の中の任意の2点iとjに対して、abs(i-j)が集合Bに属するならば、iとjの間に1本の無方向の辺がある.今、Bのいくつかの頂点を少なくとも削除して、残りの図を二分図にすることができます.
    解題構想:二分図の問題ではなく、「一枚の図は二分図であり、図中に奇環が存在しない場合のみ」という二分図判定定理を用いた.なぜ二分図アルゴリズムで解決できないのかというと,問題規模から推測できるが,図を構築するにはO((1 e 18)2)O((1 e 18)^2)O((1 e 18)2)が必要であり,nの規模では許されないことは明らかである.
    では、数学的に考えて(法則を探す)、ノード0とノードaがあれば、0からaまで辺があり、ノード2*aがまだ存在すれば、aと2*aにも辺があり、2*aと0にも辺があり、これが奇環である(3*aがあっても、奇環+偶環である)ため、2*aは存在せず、同じ理屈でも4*aは存在しないので、aを残すには、2*a、4*a、8*a、...は削除しなければならない.しかし,頂点集合は[1,1 e 18]であり,個々の計算は明らかに現実的ではないため,集合B中の各要素b,B中のどの要素がbとともに保持できるかについては複雑度が高すぎる.
    aにx個の因数2があり、bにy個の因数2(x!=y)があると、aとbは同時に存在しないに違いない.c=lcm(a,b)と仮定すると、0->a->2*a->c->2*b->b->0は必ず奇環を構成することができるので、aとbは同時に存在しない.だからx!=yは、aとbが同時に存在しない(aとbは共に集合B内の要素である).
    x=yであれば、aとbは同時に存在してもよい.aがbの倍数である場合、またはbがaの倍数である場合、0を削除することはできないので、リングがあれば必ず双環である.
    以上のように,B集合中の各要素にどれだけの因子2があるかを統計することで,最大で同時にどれだけの要素が存在するか,すなわち少なくともどれだけの要素が削除されるかを判断できる.コードの例:
    #include
    const int N = 2e5+10;
    typedef __int64 ll;
    ll a[N];
    int tc[N],num[N],n;
    void solve(){
    	for(int i = 1;i <= n;i++){
    		ll tmp = a[i];
    		while(tmp && tmp%2 == 0) tc[i]++,tmp/=2;
    		num[tc[i]]++;
    	}
    	int p = 0;	 
    	for(int i = 1;i <= 64;i++)
    		if(num[p] < num[i]) p = i;
    	printf("%d
    "
    ,n-num[p]); for(int i = 1;i <= n;i++) if(tc[i] != p) printf("%I64d ",a[i]); } int main(){ scanf("%d",&n); for(int i = 1;i <= n;i++) scanf("%I64d",a+i); solve(); return 0; }