Codeforces Round #548 (Div. 2) 题解

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

 

A. Even Substrings

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

#include 
#include 
#include 
#include 
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 
#include 
#include 
#include 
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
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair 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

D. Steps to One

解法一:期望DP

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

                                dp[x] = 1 + \sum_{i=1}^{m}\frac{dp[gcd(x,i)]}{m}

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

                             \frac{(1-m/x)}{m}*dp[x] = 1 + \sum_{d|x}^{m} \frac{dp[d]*c[d]}{m}

其中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 
using namespace std;
typedef long long int ll;
const int mod = 1e9 + 7;
const int mx = 1e5 + 10;
int m,cnt[mx];
vector  g[mx];
ll dp[mx]; 
bool vis[mx];
void init()
{
	for(int i=mx/2;i>1;i--){
		for(int j=i*2;j>= 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的期望,那么这个期望也就符合负二项分布的概率期望\frac{rp}{1-p}。一个·事件有概率p成功,实验进行失败r次后就结束得期望次数,这里r = 1,p = n / x / n。接下来是我的个人理解:

对于原问题来说,肯定至少长度为1.所以我们假设第一个已经取完了,这里也不用考虑它取的是什么值。接下来考虑一个序列的gcd等于1,那么它的gcd肯定是越变越小小到了1.所以也就是1 + \sum_{x = 2}^{m}E(x),但是对于上面的求负二项的概率期望的公式来说,当求x = 2时,实际求得是概率期望是2的倍数的不是2的,也就是包含了4,6,8...等的,所以我们这要算质数就可以了,最后还要加个容斥,因为6即被2包括了也被3包括了。

#include 
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  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

E. Maximize Mex

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

#include 
#define fi first
#define se second 
using namespace std;
typedef long long int ll;
typedef pair  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  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) 题解_第1张图片

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

#include 
using namespace std;
typedef pair  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

 

你可能感兴趣的:(题集,树状数组,DP,数学期望,二分匹配,数据结构,思维题,离线)