Educational Codeforces Round 186 C-F 个人题解

Educational Codeforces Round 186

C - Production of Snowmen

给定三个分别有 \(n\) 个元素的环 \(a,b,c\),现在要选择一个三元组 \((i,j,k),1\le i,j,k\le n\),使得 \((a_i,b_j,c_k),(a_{i+1},b_{j+1},c_{k+1}),\cdots,(a_{i+n-1},b_{j+n-1},c_{k+n-1})\) 都满足 \(a<b<c\),问合法的三元组个数。

\(1\le n\le 5\times 10^3\)

先考虑只有两个环的情况怎么做。

先断环为链,不难想到先 \(O(n^2)\) 枚举起点再 \(O(n)\) 比较,总共 \(O(n^3)\)

但会发现有很多重复计算的东西。由于这玩意类似滑动窗口,考虑换一下枚举的东西,我们先枚举两个起点 \(i,j\) 的距离 \(dis\),然后再枚举 \(i\)。你会发现当我们的 \(i\) 往后挪一位时,\(j\) 也同步挪一位,此时刚好有一对数进入了我们的滑动窗口,有一对数出去了,那我们只需要比较出去的这对和进来的这对就行了。这样下来时间复杂度是 \(O(n^2)\) 的。

于是我们可以求出来对于每个 \(j\),合法的二元组 \((j,k),b_j<c_k\) 的个数,这里设为 \(p_j\)。我们对 \(a\) 环和 \(b\) 环施以同样的比较方式,如果有两个起点 \(i,j\) 合法,我们为答案加上 \(p_j\) 即可。

总时间复杂度 \(O(n^2)\)

ll T;
ll n,a[N*2],b[N*2],c[N*2];
ll p[N*2];
void solve(){
	cin>>n;
	for(ll i=1;i<=n;i++) p[i]=0;
	for(ll i=1;i<=n;i++){ cin>>a[i]; a[i+n]=a[i]; }
	for(ll i=1;i<=n;i++){ cin>>b[i]; b[i+n]=b[i]; }
	for(ll i=1;i<=n;i++){ cin>>c[i]; c[i+n]=c[i]; }
	for(ll dis=0;dis<n;dis++){
		ll cnt=0;
		for(ll i=1;i<=n;i++){
			ll j=i+dis;
			if(b[i]>=c[j]) cnt++;
		}
		if(!cnt) p[1]++;
		for(ll i=2,j=dis+2;i<=n;i++,j++){
			if(b[i-1]>=c[j-1]) cnt--;
			if(j>n) j-=n;
			if(b[i+n-1]>=c[j+n-1]) cnt++;
			if(!cnt) p[i]++;
		}
	}
	for(ll i=1;i<=n;i++) p[i+n]=p[i];
	ll ans=0;
	for(ll dis=0;dis<n;dis++){
		ll cnt=0;
		for(ll i=1;i<=n;i++){
			ll j=i+dis;
			if(a[i]>=b[j]) cnt++;
		}
		if(!cnt) ans+=p[dis+1];
		for(ll i=2,j=dis+2;i<=n;i++,j++){
			if(a[i-1]>=b[j-1]) cnt--;
			if(j>n) j-=n;
			if(a[i+n-1]>=b[j+n-1]) cnt++;
			if(!cnt) ans+=p[j];
		}
	}
	cout<<ans<<"\n";
}

D - Christmas Tree Decoration

现在有 \(n+1\) 个从 \(0\) 编号到 \(n\) 的盒子,每个盒子里有 \(a_i\) 个装饰品。有 \(n\) 个人,按照排列 \(p\) 的顺序,从 \(p_1\)\(p_n\) 依次轮流拿装饰品挂上去,第 \(i\) 个人有两种选择:

  • 盒子 \(0\) 里选一个礼物挂上去;
  • 盒子 \(i\) 里选一个礼物挂上去。

如果没有出现一个人没有礼物可挂的情况,这个排列就是合法的,问合法的排列数,答案模 \(998244353\)

\(1\le T\le 5000,1\le n\le 50\)

你发现 \(0\) 号盒子可以看作一个备用盒子嘛,低保金属于是。

于是发现,每次等到第 \(i\) 个人对应的盒子里没有了再去拿 \(0\) 号盒子里的东西,一定不劣,相当于按需所取。于是题意转化为,求轮空次数 \(\le a_0\) 的排列数。

首先什么时候轮空次数最少?你会发现一定是 \(p_i\) 按照 \(a_{p_i}\) 从大到小的顺序排列,例如 3 3 2 1 1,你会发现这样子后面的 2 1 1 就可以少轮空一次。此时总轮空次数为

\[p=\sum_{i=1,a_i<maxa}maxa-1-a_i \]

其中 \(maxa=\max a\)

因此我们先把 \(a_1\sim a_n\) 排个序,这样不影响对答案的计数。

然后你会发现,每当有一个小于 \(maxa\) 的元素被丢到 \(maxa\) 的前面去,如 1 3 3 2 13 2 3 1 1,轮空次数就会加一,因此我们枚举轮空次数,设轮空次数最小值为上文的 \(p\),小于 \(maxa\) 的元素个数为 \(nmx\),等于 \(maxa\) 的元素个数为 \(mx\),则方案数可以推出来:

\[\sum_{i=p}^{\min(a_0,p+nmx)}\binom{nmx}{i-p}\cdot(mx-1+i-p)!\cdot mx\cdot (nmx-(i-p)) \]

意思是:

  • 最右边的 \(maxa\) 元素之前的所有元素可以乱排;
  • 最右边的 \(maxa\) 元素有 \(mx\) 种选法;
  • 右边的数可以乱排。

时间复杂度 \(O(Tn)\)

void solve(){
	cin>>n>>a0;
	fac[0]=1;
	for(ll i=1;i<=n;i++){
		cin>>a[i];
		fac[i]=fac[i-1]*i%P;
	}
	finv[n]=qpow(fac[n],P-2);
	for(ll i=n-1;i>=0;i--) finv[i]=finv[i+1]*(i+1)%P;
	sort(a+1,a+n+1,cmp);
	ll mxcnt=0,nmxcnt=0,bas=0;
	for(ll i=1;i<=n;i++){
		if(a[i]==a[1]) mxcnt++;
		else nmxcnt++,bas+=a[1]-1-a[i];
	}
	ll ans=0;
	for(ll i=0;i+bas<=a0&&i<=nmxcnt;i++) ans=(ans+C(i,nmxcnt)*mxcnt%P*fac[mxcnt-1+i]%P*fac[nmxcnt-i]%P)%P;
	cout<<ans<<"\n";
}

E - New Year's Gifts

看错题了。

小 M 有 \(n\) 个朋友,他要给他的每个朋友送一份新年礼物,他已经给他们分别选好了礼物,价值为 \(y_i\)

同时,现在有 \(m\) 个免费的礼盒,每个礼盒颜值为 \(x_i\),一个礼物可以选择包装,也可以不包装。

一个朋友会开心,以下条件满足其一即可:

  • 礼物价值至少 \(z_i(z_i>y_i)\)
  • 礼盒颜值至少 \(x_i\)

现在小 \(M\) 手上有 \(k\) 个金币,问最多能让多少朋友开心。

\(1\le n,m\le 2\times 10^5,\sum\limits_{i=1}^{n}y_i \le k\le 10^{15}\)

先把底款 \(y_i\) 付了,操作一转化成加钱,加 \(z_i'=z_i-y_i\)

首先这个礼盒居然是免费的,不用白不用,把这些礼盒分配给 \(z_i'\) 最大的那几个一定不劣,用 multiset 维护最小的大于等于其 \(x_i\) 的礼盒即可。剩下的钱 \(k'\) 从小到大给最小的几个 \(z_i'\) 加钱就行了。

ll n,m,k,vis[N];
multiset<ll> box;
struct Node{
	ll x,z;
}a[N];
bool cmp(Node X,Node Y){
	return X.z<Y.z;
}
void solve(){
	box.clear();
	cin>>n>>m>>k;
	for(ll i=1;i<=m;i++){
		ll pp; cin>>pp;
		box.insert(pp);
	}
	for(ll i=1;i<=n;i++){
		ll x,y,z; cin>>x>>y>>z;
		z-=y,k-=y;
		a[i]=(Node){x,z};
		vis[i]=0;
	}
	sort(a+1,a+n+1,cmp);
	ll ans=0;
	for(ll i=n;i>=1;i--){
		auto it=box.lower_bound(a[i].x);
		if(it==box.end()) continue;
		ans++,vis[i]=1;
		box.erase(it);
	}
	for(ll i=1;i<=n;i++){
		if(vis[i]) continue;
		if(k>=a[i].z){
			ans++;
			k-=a[i].z;
		}
	}
	cout<<ans<<"\n";
}

F - Christmas Reindeer

给定 \(n\) 只鹿,每只鹿能力值为 \(2^{c_i}\),有 \(m\) 次三种询问:

  1. 添加一只能力值为 \(2^x\) 的鹿;
  2. 移除一只能力值为 \(2^x\) 的鹿(\(x\)\(\le 60\));
  3. 定义一群鹿的承载量如下,将它们的能力值从大到小排序,其总承载力为 \(S_0+\lfloor\frac {S_1}{2^1}\rfloor+\lfloor\frac {S_2}{2^2}\rfloor+\cdots+\lfloor\frac {S_{|S|}}{2^{|S|}}\rfloor\),问你有多少种选择方案使得鹿的承载力大于等于 \(x(1\le x\le 1\times 10^{18})\)

\(n,m\le 3\times 10^5\)

首先你拿一个桶去存能力值,这里记作 \(\{cnt_i \}\)

接着你考虑一种能力值的累计方案 \(r_i\) 使得其承载力刚好等于 \(x\),这里 \(r_i\) 表示能力值为 \(i\) 的有多少条鹿。

你会发现,只要字典序大于等于这玩意的累计方案,不都可以吗??因此我们对这个计数就行了,考虑枚举啥,自然想到枚举第一个大于 \(r_i\) 的位置 \(i\),此时这个累计方案被分为了三段。

\(\text{Part A}\):位置在 \(i\) 之前的。

随便选,设 \(sum_i\)\(\sum\limits_{i=1}^n cnt_i\),则有 \(a=2^{sum_{i-1}}\)

\(\text{Part B}\):位置 \(i\)

此时要求这个位置选的鹿的个数必须大于 \(r_i\)。则有

\[b=\sum_{j=r_i+1}^{cnt_i}\binom{cnt_i}{j} \]

但是这样有个问题,就是会枚举两次 \(\log V\),复杂度要炸。

因此换一下枚举的东西,正难则反,一个很巧妙的转化,就可以把时间复杂度通过 \(\sum r_i\) 弄到均摊的 \(\log V\)

\[b=2^{cnt_i}-\sum_{j=0}^{r_i}\binom{cnt_i}{j} \]

哦耶。

\(\text{Part C}\):位置在 \(i\) 之后的。

必须和 \(r_i\) 一致。

\[c=\sum_{j=i+1}^{60}\binom{cnt_j}{r_j} \]

预处理后缀即可免去再套一层 \(\log V\)

答案是 \(abc\)

总复杂度 \(O(n\log V)\)

ll qpow(ll a,ll k){
	ll res=1;
	while(k){
		if(k&1) res=res*a%P;
		a=a*a%P,k>>=1;
	}
	return res;
}
ll fac[N<<1],finv[N<<1];
ll C(ll a,ll b){
	if(a>b) return 0;
	return fac[b]*finv[a]%P*finv[b-a]%P;
}
ll T,n,m;
ll tong[70],r[70];
ll suf[70];
void solve(){
	memset(tong,0,sizeof(tong));
	cin>>n>>m;
	for(ll i=1;i<=n;i++){
		ll x; cin>>x;
		tong[x]++;
	}
	while(m--){
		ll op,x; cin>>op>>x;
		if(op==1) tong[x]++;
		else if(op==2) tong[x]--;
		else{
			for(ll i=0;i<=60;i++) r[i]=0;
			ll tmp=0;
			while(x) r[tmp++]=x&1,x>>=1;
			tmp=0;
			for(ll i=60;i>=0;i--) if(r[i]) r[i]=0,r[i+tmp]++,tmp++;
			ll ans=0,sum=0;
			suf[61]=1;
			for(ll i=60;i>=0;i--) suf[i]=suf[i+1]*C(r[i],tong[i])%P;
			for(ll i=0;i<=60;i++){
				ll a=qpow(2,sum);
				ll b=qpow(2,tong[i]);
				for(ll j=0;j<=r[i];j++){
					b=(b-C(j,tong[i])+P)%P;
				}
				ll c=suf[i+1];
				ans=(ans+a*b%P*c%P)%P;
				sum+=tong[i];
			}
			ll ans2=1;
			for(ll i=0;i<=60;i++) ans2=ans2*C(r[i],tong[i])%P;
			ans=(ans+ans2)%P;
			cout<<ans<<"\n";
		}
	}
}
posted @ 2026-01-16 22:17  xzy_sf  阅读(0)  评论(0)    收藏  举报