Loading

Codeforces Round #781 (Div. 2)

A

\(\circlearrowright\) 给出 \(n\),要求构造 \(4\) 个整数 \(a,b,c,d\),满足:\(a+b+c+d=n\)\(\gcd(a,b)=\operatorname{lcm}(c,d)\)

我教你构造一个 \(n-3,1,1,1\)

My Code
void solve(){
	int n;cin>>n;
	cout<<n-3<<' '<<1<<' '<<1<<' '<<1<<'\n';
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;) solve();
	return 0;
}

B

\(\circlearrowright\) 给你一个序列,你可以复制任何一个序列或者交换任何两个序列中的任何两个元素。求最少操作次数使得某一个序列全部相等。

容易想到让序列中的数都等于出现次数最多的那个数。然后你发现你每次可以倍增它的数量,所以就是你需要有 \(\log_2\dfrac{n}{mx}\) 次用于复制序列,剩下的就是交换。细节上用倍增不要直接算。

My Code
const int MAXN=1e5+10;
map<int,int> mp;
void solve(){
	int n,a,mx=0;cin>>n;
	mp.clear();
	rep(i,1,n){
		cin>>a;mp[a]++;
		mx=max(mx,mp[a]);
	}int cur=0;
	while(mx*(1ll<<cur)<n)
		cur++;
	cout<<n-mx+cur<<'\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;) solve();
	return 0;
}

C

\(\circlearrowright\) 给你一棵树,节点初始都是白色,每秒可以选择一个节点把它染黑。然后每秒对于一个节点 \(u\),如果它至少一个儿子是黑色的,那么它会自发地把剩下的白色儿子染黑。求把整棵树都染黑的最短时间是多少。

把节点按父亲分类,容易想到一个贪心,先染最多的,然后让它自己扩散,最后如果每一组都染过了,那就回来补刀。具体做就是排序从大到小,然后先每一组染一个,算出都染过之后每一组剩下有几个白点。然后你枚举一下用几秒可以把剩下的白点都染黑,用前缀和判一下就可以了。

My Code
const int MAXN=2e5+10;
int son[MAXN],cnt[MAXN];
void solve(){
	int n;
	cin>>n;
	rep(i,0,n) son[i]=cnt[i]=0;
	son[0]=1;
	rep(i,2,n){
		int p;cin>>p;
		son[p]++;
	}
	sort(son,son+1+n,[&](int x,int y){return x>y;});
	int stp=0;
	rep(i,0,n)
		if(son[i]==0){
			stp=i-1;break;
		}
	rep(i,0,stp) son[i]-=min(son[i],stp+1-i);
	int sum=0;
	rep(i,0,stp) sum+=son[i];
	if(!sum){
		cout<<stp+1<<'\n';
		return;
	}
	sort(son,son+1+n,[&](int x,int y){return x>y;});
	int brk=0;
	rep(i,0,n)
		if(son[i]==0){
			brk=i;break;
		}
	int lst=1;
	per(i,brk-1,0){
		rep(j,lst,son[i]){
			cnt[j]=cnt[j-1]+i+1;
		}lst=son[i]+1;
	}
	rep(i,0,son[0]){
		if(sum-cnt[i]<=i){
			cout<<i+stp+1<<'\n';
			return;
		}
	}
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;) solve();
	return 0;
}

D

\(\circlearrowright\) 交互猜数,每次询问 \(\gcd(x+a,x+b)\),最多询问 \(30\) 次。

看到 \(30\),看到 \(2\times 10^9\) 就不难想到二进制。首先每次询问相当于是说,你可以知道 \(\gcd(x+b,a-b)\)(辗转相减),然后考虑从低到高逐位确定。我们考虑令 \(b\) 加到 \(x\) 上之后,恰好在当前考虑的第 \(i\) 位产生一个进位。然后如果第 \(i\) 位本来是 \(1\),那么接下来询问 \(x+b\)\(2^{i+1}\)\(\gcd\) 就是 \(2^{i+1}\),否则就是 \(2^i\)。然后就这样做 \(30\) 次就可以全部猜出来了。

My Code
int qry(int a,int b){
	cout<<"? "<<a<<' '<<b<<endl;
	int g;cin>>g;return g;
}
void solve(){
	int x=0;
	rep(i,1,30){
		int b=(1ll<<(i-1))-x,v=(1ll<<i);
		int g=qry(v+b,b);
		if(g==v) x+=(1ll<<(i-1));
	}cout<<"! "<<x<<endl;
}
signed main()
{
	int T;for(cin>>T;T--;) solve();
	return 0;
}

E

\(\circlearrowright\) 给你一个序列 \(a\),每次询问给出一个 \(l,r\),要求区间内按位或起来最小的一对数。求他们的或和。

这题的做法是,找区间内最小的 \(31\) 个数,然后暴力求这 \(31\) 个数两两或起来的最小值。我们可以归纳证明这个结论。结论:对于小于 \(2^k\) 的数,我们在 \(k+1\) 个最小的数中可以找出两个数或起来最小就是整段的最小。

首先,当 \(k=1\) 时,显然有 \(a_i\in {0,1}\),你掏出最小的两个数显然就是最小的。

接下来,我们考虑,如果 \(k\) 是成立的,那么 \(k+1\) 是否成立。假如说,所有数二进制的第 \(k+1\) 位都是 \(1\),那么最终或起来,答案的第 \(k+1\) 位肯定是 \(1\),那么我们只要管接下来的 \(k\) 位就可以了,也就是只需要 \(k+1\) 个数(而我们当前论证中可选 \(k+2\) 个数,肯定是够的)。如果有一个数不是 \(1\),那么我们考虑第 \(k+1\) 位一定还是 \(1\),但是我们选最小的数的时候一定会选择那个第 \(k\) 位不是 \(1\) 的,但它不一定最优,所以我们会需要多选一个,也就是 \(k+2\) 个。如果有多于两个数不是 \(1\),那么答案第 \(k+1\) 位肯定是 \(0\),于是我们直接选最小的 \(k+1\) 个数,肯定包含了答案。

My Code
const int MAXN=1e5+10;
struct info{
	vector<int> mn;
	void clear(){mn.clear();}
	info friend operator+(info a,info b){
		info ret;
		int at=0,bt=0;
		while(ret.mn.size()<31&&at<a.mn.size()&&bt<b.mn.size())
			if(a.mn[at]<b.mn[bt]) ret.mn.pb(a.mn[at++]);
			else ret.mn.pb(b.mn[bt++]);
		while(ret.mn.size()<31&&at<a.mn.size()) ret.mn.pb(a.mn[at++]);
		while(ret.mn.size()<31&&bt<b.mn.size()) ret.mn.pb(b.mn[bt++]);
		return ret;
	}void push(int x){mn.clear();mn.pb(x);}
};
struct Tree{int l,r;info mn;}tr[MAXN<<2];
#define ls i<<1
#define rs i<<1|1
int a[MAXN];
void pushup(int i){tr[i].mn=tr[ls].mn+tr[rs].mn;}
void build(int i,int l,int r){
	tr[i].l=l;tr[i].r=r;if(l==r){tr[i].mn.push(a[l]);return;}
	int mid=(l+r)>>1;build(ls,l,mid);build(rs,mid+1,r);pushup(i);
}info ask(int i,int l,int r){
	if(tr[i].l==l&&tr[i].r==r) return tr[i].mn;
	int mid=(tr[i].l+tr[i].r)>>1;
	if(r<=mid) return ask(ls,l,r);
	else if(l>mid) return ask(rs,l,r);
	else return ask(ls,l,mid)+ask(rs,mid+1,r);
}
void solve(){
	int n;cin>>n;
	rep(i,1,n) cin>>a[i];
	build(1,1,n);
	int Q,l,r;cin>>Q;
	while(Q--){
		cin>>l>>r;
		vector<int> cur=ask(1,l,r).mn;
		int ans=INF;
		rep(i,0,(int)cur.size()-1)
			rep(j,i+1,(int)cur.size()-1)
				ans=min(ans,cur[i]|cur[j]);
		cout<<ans<<'\n';
	}
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;) solve();
	return 0;
}
posted @ 2022-04-09 14:52  ZCETHAN  阅读(38)  评论(0编辑  收藏  举报