Codeforces Round #548 (Div. 2) 题解

奋斗吧
奋斗吧
擅长邻域:未填写

标签: Codeforces Round #548 (Div. 2) 题解 JavaScript博客 51CTO博客

2023-05-31 18:24:21 70浏览

Codeforces Round #548 (Div. 2) 题解,题目链接http://codeforces.com/contest/1139A.EvenSubstrings判断个位是否是奇数即可。#include#include#include


题目链接http://codeforces.com/contest/1139

 

A. Even Substrings

判断个位是否是奇数即可。

#include <iostream>
#include <set>
#include <array>
#include <vector>
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
ll dp[mx][2]; 
char str[mx];
int main()
{
	int n;
	scanf("%d%s",&n,str+1);
	ll ans = 0;
	for(int i=1;i<=n;i++){
		int v = str[i] - '0';
		if(v%2==0) ans += i;
	}
	printf("%I64d\n",ans);
    return 0;
}

B. Chocolates

倒过来看的话,直接贪心就OK了。

#include <iostream>
#include <set>
#include <array>
#include <vector>
using namespace std;
typedef long long ll;
const int mx = 2e5 + 10;
int a[mx];
int main()
{
	int n;
	ll ans = 0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",a+i);
	ans += a[n];
	for(int i=n-1,v=a[n];i;i--) 
	{
		v = max(0,min(v-1,a[i]));
		ans += v;
	}
	printf("%I64d\n",ans);
    return 0;
}

C. Edgy Trees

总共有n^k种选法,那么如果我们能找出不合法的方案数ret,那么最终的答案就是n^k-ret。

可以用并查集或者dfs将黑色边把图分成若干的块,那么肯定如果k个点都在某个块中,那么所有路径肯定都不会经过黑色边了。

所以无效的方案数就是枚举所有块,然后所有块的个数的k次方累加就是无效方案数了。

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,bool> pa;
const int mx = 1e5 + 10;
const int mod = 1e9 + 7;
int n,m,fa[mx],cnt[mx];
bool vis[mx];
int find(int x){
	return x==fa[x]? x:fa[x]=find(fa[x]);
}
ll qpow(ll x,ll y)
{
	ll ans = 1;
	while(y){
		if(y&1) ans = ans*x%mod;
		x = x*x%mod;
		y >>= 1;
	}
	return ans;
}
int main()
{
	scanf("%d%d",&n,&m);
	int u,v,w;
	for(int i=1;i<=n;i++) cnt[i] = 1,fa[i] = i;
	for(int i=1;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);
		if(!w){
			u = find(u),v = find(v);
			cnt[u] += cnt[v];
			fa[v] = u;
		}
	}
	ll ans = 0;
	for(int i=1;i<=n;i++)
	if(i==find(i)) ans = (ans + qpow(cnt[i],m))%mod;
	printf("%I64d\n",(qpow(n,m)+mod-ans)%mod);
    return 0;
}

D. Steps to One

解法一:期望DP

设dp[x]表示当前序列的gcd是x的情况下,还有可以写多长到达1的期望。所以很明显式子就是:

                                

Codeforces Round #548 (Div. 2) 题解_i++

如果当当这样的话是O(n*2)会超时,但是我们从式子中可以看出dp[x]只会从它的因子转移,所以我们就直接去找x的因子就好了,那么就有:

                             

Codeforces Round #548 (Div. 2) 题解_c++_02

其中d是不等x的x的因子,c[d]表示[1,m]与x的gcd是d的个数。而因子我们可以预处理,然后可以用容斥可以求出c[d]。

因为x的因子不会超过2^6个,所以我们可以从大到小枚举因子,然后容斥,时间复杂度O(2^6*2^6)。

还有一个问题,上面说的dp[x]表示之后的长度期望,那么之前的就不用算了嘛?事实上我们只要确定序列的第一个数不就好了吗?所以最后会是1/m(1+dp[1])+1/m(1+dp[2])+1/m(1+dp[3])+...+1/m(1+dp[m])。

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int mod = 1e9 + 7;
const int mx = 1e5 + 10;
int m,cnt[mx];
vector <int> g[mx];
ll dp[mx]; 
bool vis[mx];
void init()
{
	for(int i=mx/2;i>1;i--){
		for(int j=i*2;j<mx;j+=i){
			g[j].push_back(i);
		}
	}
} 
ll qpow(ll x,ll y){
	ll ans = 1;
	while(y){
		if(y&1) ans = ans*x%mod;
		y >>= 1;
		x = x*x%mod;
	}
	return ans;
}
void cut(int x){
	for(int i:g[x])
	cnt[i] -= cnt[x];
}
int main(){
	scanf("%d",&m);
	init();
	ll invm = qpow(m,mod-2);
	for(int i=2;i<=m;i++){
		ll ret = 1;
		cnt[i] = m / i;
		for(int j:g[i]) cnt[j] = m / j;
		cut(i);
		for(int j:g[i]){
			cut(j);
			ret = (ret + cnt[j]*invm%mod*dp[j]%mod)%mod;
		}
		dp[i] = ret * m % mod * qpow(m-m/i,mod-2)%mod;
	}
	ll ans = 1;
	for(int i=2;i<=m;i++){
		ans = (ans + dp[i]*invm%mod)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}

解法二:负二项分布期望

现在我们换一种思路,假设现在序列的gcd是x,那么我接下来选一个数,使得该序列的gcd还是x的选择有n / x,反之不是的选择有n - n / x,因此我们可以设一个E(x}表示为现在gcd是x直到gcd小于x的期望,那么这个期望也就符合负二项分布的概率期望

Codeforces Round #548 (Div. 2) 题解_i++_03

。一个·事件有概率p成功,实验进行失败r次后就结束得期望次数,这里r = 1,p = n / x / n。接下来是我的个人理解:对于原问题来说,肯定至少长度为1.所以我们假设第一个已经取完了,这里也不用考虑它取的是什么值。接下来考虑一个序列的gcd等于1,那么它的gcd肯定是越变越小小到了1.所以也就是

Codeforces Round #548 (Div. 2) 题解_c++_04

,但是对于上面的求负二项的概率期望的公式来说,当求x = 2时,实际求得是概率期望是2的倍数的不是2的,也就是包含了4,6,8...等的,所以我们这要算质数就可以了,最后还要加个容斥,因为6即被2包括了也被3包括了。

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int mod = 1e9 + 7;
const int mx = 1e5 + 10;
int m,pri[mx],top,mu[mx];
vector <int> g[mx]; 
bool vis[mx];
ll qpow(ll x,ll y){
	ll ans = 1;
	while(y){
		if(y&1) ans = ans*x%mod;
		y >>= 1;
		x = x*x%mod;
	}
	return ans;
}
void init(){
	for(int i=2;i<mx;i++){
		if(!vis[i]){
			pri[top++] = i;
			mu[i] = -1;
		}
		for(int j=0;j<top&&pri[j]*i<mx;j++){
			vis[pri[j]*i] = 1;
			if(i%pri[j]==0){
				mu[pri[j]*i] = 0;
				break;
			}
			mu[pri[j]*i] = -mu[i];
		}
	}
}
int main(){
	scanf("%d",&m);
	init();
	ll invm = qpow(m,mod-2);
	ll ans = 1;
	for(int i=2;i<=m;i++){
		int f = m / i;
		ans = (ans - mu[i]*f*qpow(m-f,mod-2)%mod)%mod; 
	}
	printf("%lld\n",(ans+mod)%mod);
	return 0;
}

E. Maximize Mex

根据题目意思可以想到二分匹配来做,但是有d,n^3肯定是不行的,所以离线一下不就是O(n^2)的吗。

#include <bits/stdc++.h>
#define fi first
#define se second 
using namespace std;
typedef long long int ll;
typedef pair <int,int> pa;
const int mod = 1e9 + 7;
const int mx = 5e3 + 10;
const int N = 5000;
int n,m,a[mx],cp[mx];
int b[mx],d[mx],ans[mx];
bool vis[mx];
vector <int> g[mx];
bool pipei(int u){
	for(int v:g[u]){
		if(!vis[v]){
			vis[v] = 1;
			if(cp[v]==-1||pipei(cp[v]))
			{
				cp[v] = u;
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	scanf("%d%d",&n,&m);
	int v,q;
	memset(cp,-1,sizeof(cp));
	for(int i=1;i<=n;i++) scanf("%d",a+i);
	for(int i=1;i<=n;i++){
		scanf("%d",b+i);
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++) scanf("%d",d+i),vis[d[i]] = 1;
	for(int i=1;i<=n;i++)
	if(!vis[i]) g[a[i]].push_back(b[i]);
	int j = 0;
	for(int i=q;i>=1;i--){
		for(;j<5000;j++){
			memset(vis,0,sizeof(vis));
			if(!pipei(j)) break;
		}
		g[a[d[i]]].push_back(b[d[i]]);
		ans[i] = j;
	}
	for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
	return 0;
}

F. Dish Shopping

如果把人的price和beauty放在二维直角坐标系上,那么此时加入一个dish(p,s,b).坐标系上被更新的点就是(p,b),(s,b-s+p),(s,b+s-p)这三个点形成的三角范围内的所有点,而且这个三角形是关于y = b对称。

对称性的特点就是三角形内点的分布(p,b),(p+1,b-1),(p+1,b),(p+1,b+1)...就是每一列都比上一列左右+1。O(n)更新时不可能的,一定会超时。所以我们想怎么在log的复杂度内更新一个dish。那么就考虑这个关于y = b对称的三角区域的特性。

用两个数组la,ra来解决这个问题。当我们更新(p,b)这个点时,两个数组操作为la[p+b]++,ra[b+1-p]--。有点像数组的差分。

那么更新区域等于到二维直角坐标系上就是这样的:

Codeforces Round #548 (Div. 2) 题解_c++_05

不就是关于y = b对称的这个三角形区域吗?只不过他是无穷大的而已,那么我们就可以按x轴排序,对于一个dish(p,s,b)到达p时更新,到达s时消除更新就可以了,这个可以用树状数组来做,至于坐标系上的点,离散化一下就可以了。

#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pa;
typedef long long ll;
const int mx = 1e5 + 10;
int n,m,p[mx],s[mx],b[mx];
int c[mx],f[mx],is,tot,ans[mx];
int a[mx<<2],sum[mx<<2][2];
struct node{
	int v,id;
	int u;
	bool operator < (node A)const{
		if(v==A.v) return u < A.u;
		return v < A.v;
	}
}mp[mx<<2];
int getP(int x){
	return lower_bound(a+1,a+is,x) - a;
}
void add(int x,int id,int v){
	x = getP(x);
	while(x<is){
		sum[x][id] += v;
		x += x&(-x);
	}
}
int get(int x,int id){
	int ans = 0;
	x = getP(x);
	while(x){
		ans += sum[x][id];
		x -= x&(-x);
	}
	return ans;
}
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",p+i);
		mp[tot++] = {p[i],i,0};	
	}
	for(int i=1;i<=n;i++){
		scanf("%d",s+i);
		mp[tot++] = {s[i],i,2};	
	}
	for(int i=1;i<=n;i++){
		scanf("%d",b+i);
		a[++is] = b[i] + p[i];
		a[++is] = b[i] + 1 - p[i];
	}
	for(int i=1;i<=m;i++){
		scanf("%d",c+i);
		mp[tot++] = {c[i],i,1};	
	}
	for(int i=1;i<=m;i++){
		scanf("%d",f+i);
		a[++is] = f[i] + c[i];
		a[++is] = f[i] - c[i];  
	}
	sort(mp,mp+tot);
	sort(a+1,a+1+is);
	is = unique(a+1,a+1+is) - a;
	for(int i=0;i<tot;i++){
		int id = mp[i].id;
		if(mp[i].u&1)
		ans[id] = get(c[id]+f[id],0) + get(f[id]-c[id],1);
		else{
			add(b[id]+p[id],0,mp[i].u?-1:1);
			add(b[id]+1-p[id],1,mp[i].u?1:-1);
		}
	}
	for(int i=1;i<=m;i++) printf("%d ",ans[i]);
	return 0;
}

 

好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695