kmjp's blog

競技プログラミング参加記です

yukicoder : No.2081 Make a Test Case of GCD Subset

GCDの問題、いろいろあるなぁ。
https://yukicoder.me/problems/no/2081

問題

整数Mが与えられる。
1~100000の範囲の整数の部分集合Sのうち、以下を満たすものを998244353で割った余りがMとなるものを1つ答えよ。

  • Sの部分集合のうち、GCDが2以上になるもの

解法

素数aに対し、aおよびaに(aと素な)素数を掛けたものを計n個準備すれば、GCDがaとなるものを(2^n)-1手配できる。
(2^n)-1≦Mとなるnに対し、未使用の素数をn個使う、ということを繰り返せばよい。

const int prime_max = 100000;
set<int> prime;
int NP,divp[prime_max];

void cprime() {
	if(NP) return;
	for(int i=2;i<prime_max;i++) if(divp[i]==0) {
		//M[i]=NP;
		prime.insert(i); NP++;
		for(ll j=1LL*i*i;j>=i&&j<prime_max;j+=i) if(divp[j]==0) divp[j]=i;
	}
}

ll M;

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>M;
	cprime();
	vector<int> V;
	
	if(M==0) V={1};
	
	while(M) {
		int n=0;
		while((1<<(n+1))-1<=M) n++;
		M-=(1<<n)-1;
		int a=*prime.begin();
		prime.erase(a);
		V.push_back(a);
		n--;
		while(n) {
			int b=*prime.rbegin();
			prime.erase(b);
			if(a*b<=100000) {
				n--;
				V.push_back(a*b);
			}
		}
	}
	
	
	cout<<V.size()<<endl;
	FORR(v,V) cout<<v<<" ";
	cout<<endl;
}

まとめ

思いついてしまえばすぐ。