Particle

競技プログラミングについての雑記

SRM 596

250

要素がすべて0であるような配列に対して、「配列の要素一つに対して1増やす操作」(操作1)と、「配列のすべての要素を2倍する」(操作2)という操作を繰り返して、与えられた配列にする。
必要な操作の回数の最小値を求めよ。


例えば、二進法で{10100}は、操作を1,2,2,1,2,2の順で行うと6回で作れます。(最初の操作2は操作1でも良いですが、操作2にしたほうがうれしいです。)操作1は1の個数、操作2は(桁数-1)回必要です。
要素が複数の場合も同様ですが、操作2は、全体で共通なので、最も桁数が大きいものに合わせればよいです。
よって、答えは、(1の個数の和)+(最も桁数が大きい数字の桁数-1)になります。
与えられた配列がすべて0のときは、0です。

class IncrementAndDoubling {
public:
	int getMin( vector <int> s ) {
		int ans = 0,sh = 0;
		for(int i = 0; i < s.size(); i++){
			for(int j = 1; s[i]; j++,s[i]>>=1){
				if(s[i]&1){
					ans++;
					sh = max(sh,j);
				}
			}
		}

		return max(ans+sh-1,0);
	}
};

500

要素の個数がNの集合Sに含まれる任意の2つの異なる要素A,Bは、A&B > 0を満たし、任意の3つの異なる要素A,B,CはA&B&C = 0 を満たすようなSのうち、辞書順最小のものを出力する。(解がないときは、空の配列を返す)

与えられたものが条件を満たしているかを確認したあと、貪欲法で解けます。
A&B&C = 0 は、X&(1LL<

int used[100];
int M = 60;

class BitwiseAnd {
public:
	vector<long long> lexSmallest( vector<long long> s, int N ) {
		vector<ll> emp;
		memset(used,0,sizeof(used));
		if(s.empty()) s.push_back((1LL<<(N-1))-1);
		for(int i = 0; i < s.size(); i++){
			for(int j = 0; j < i; j++){
				ll p = s[i]&s[j];
				if(!p) return emp;
				for(int k = 0; k < j; k++){
					if(p&s[k]) return emp;
				}
			}
		}
		for(int i = 0; i < s.size(); i++){
			int cnt = 0;
			for(int j = 0; j < M; j++){
				if(s[i]&(1LL<<j)){
					used[j]++;
					cnt++;
					if(used[j]>2)return emp;
				}
			}
			if(cnt<N-1) return emp;
		}

		while(s.size()<N){
			ll t = 0;
			for(int i = 0; i < s.size(); i++){
				for(int j = 0; j <= M; j++){
					if(j==M) return emp;
					if((s[i]&(1LL<<j)) && used[j]==1){
						t |= 1LL<<j;
						used[j]++;
						break;
					}
				}
			}
			for(int i = 0,j = 0; i < N-s.size()-1; i++){
				while(used[j]&&j<M)j++;
				if(j==M) return emp;
				t |= 1LL<<j;
				used[j]++;
			}
			s.push_back(t);

		}

		sort(s.begin(),s.end());
		return s;

	}
};

ox- 214.88 + 50.0 = 264.88
1742 -> 1777