Codeforces Round #548 Div2 A~C题解

闲话

这场的D题也是个神仙题,标程用的是反演套DP.由于我不会写期望,先放着等以后点了数学科技再来.然后对于这场排名来说也就是拼ABC手速了,由于VP的时候CF炸了导致我一直没提交代码就很谔谔.C题虽然比较水,但是因为一开始走错方向了导致一直没写出来,不过比以前的情况要好一点的就是现在有时候也能把ABC打出来了,虽然时间和罚时上仍然很垃圾,不过在看到一个不会的问题,逐步思考解决的方向这件事上有了一点小小的进步.

A. Even Substrings

原题大意:给定了一个字符串s,现在要求这段字符串里连续的子段表示的数是偶数的个数,注意只要是位置不同就算不同.
数据范围:
\(1 \leq |s| \leq 65000\)

思路

显然对于一个表示出来是偶数的数,末尾一定是偶数,则对于字符串里的每一个偶数考虑以他为结尾的偶数有多少个,最后累加起来就得到了答案,比较简单.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
	ios::sync_with_stdio(0);cin.tie(0);
	int n;cin >> n;
	string s;cin >> s;
	ll res = 0;
	for(int i = 0;i < n;++i)	
		if((s[i] - '0') % 2 == 0)
    		res += i +1;
    cout << res;
    return 0;
}

B. Chocolates

原题大意:商店里面有\(n\)种巧克力,对于\(i\)种巧克力有\(a_i\)个.假设手上的钱是无限的,如果购买的\(i\)种巧克力的数量是\(x_i\)的话,则整个\(x\)序列需要满足他是一个严格上升的序列或者前面都是\(0\).问在此限制下,最多能买多少个巧克力.
注意:0也要算进去.
数据范围:
\(1 \leq n \leq 2 *10^5\)

思路

显然由于题目的限制及其的强,顺理成章的就可以从最后一位开始往前推,因为最后一位如果是\(0\)的话则前面必须全部是\(0\).于是这个题的思路也就比较好想了,从最后一位开始贪心,当前这一位是之前选的和本位最多能买的数量取一个较小值,一直往前直到不能买位置.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 2e5+7;
int a[N];
ll res = 0,x = 1e9+7;
int main()
{
	int n;scanf("%d",&n);
	for(int i = 1;i <= n;++i)	scanf("%d",&a[i]);
    for(int i = n;i >= 1;--i)
    {
    	x = min(x - 1,1ll*a[i]);
    	if(x >= 0)	res += x;
    }
    printf("%lld",res);
    return 0;
}

C. Edgy Trees

原题大意:给定一个树,树上的每条边要么是黑色的要么是红色的.这样定义一个牛逼的\(k\)路径:首先整个路径里面包含\(k\)个点,这些点可以有重复,其次这样描述这条路径:顺次的从第一个元素按最短路径走到第二个元素,第二个元素再按最短路径走到第三个元素,直到结束.如果这些路径上经过了一个黑色的边,则认为这个\(k\)路径是牛逼的.现给定\(k\),求这个树上牛逼的\(k\)路径有多少个.答案对\(10^9+7\)取模.
数据范围:
\(2 \leq n \leq 10^5\)
\(2 \leq k \leq 100\)

思路

先想能不能正面突破,如果要正面突破的话,一个比较直观的想法就是看怎么直接构造出这个\(k\)路径,对于第一个点显然是\(n\)种选择,第二个点就是第一个点出发有多少个经过黑色边的路径.好到这里就有两个问题了:首先第一个点走到第二个点不一定非要经过黑边,可能是后面的,第二你根本不能从这个信息里知道第三位应该填什么.正面突破是及其困难的,考虑有没有反向的思路.
由于这个题要求的是至少,那反过来就是一个都没有,也就是说答案是全集挖掉只有白色边连接的路径,全集比较简单,就是\(n^k\).考虑怎么求出树上只有白色边连接的路径,显然可以类似缩点的方式,把树上只有白色边相连的点看成是一个点,缩完之后的树上只有黑边,那么显然如果跨点选择必然导致走过一个黑边,因此答案就是这些划分完之后的集合里自己可以组合出多少个\(k\)路径.假设一个缩完的集合里有\(a\)个数,那么要搞出一个\(k\)路径也就是在这个集合里面有重复的选择出\(k\)个元素,显然有\(k\)部,每一步有\(a\)个选择,因此对这个集合来说,他能构造出来的\(k\)路径就有\(a^k\)条,不同的集合之间不能相连,因此加法原理适用,最后所有的加起来再被全集一减,答案就出来了.
特别注意由于是一个减法的形式,一定要注意对负数取模,虽然这个题应该是不会出错的,但是一定要注意这点.负数取模的问题往往比较隐藏.只有平常多加细心考虑.

思路

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+7,M = 2 * N,MOD = 1e9+7;
int edge[M],succ[M],color[M],ver[N],idx;
int id[N];
ll cnt[N];
void dfs(int u)
{
	for(int i = ver[u];~i;i = succ[i])
	{
		int v = edge[i];
		if(id[v] || color[i] == 1)	continue;
		id[v] = id[u];
		dfs(v);
	}
}
void add(int u,int v,int col)
{
	edge[idx] = v;
	color[idx] = col;
	succ[idx] = ver[u];
	ver[u] = idx++;
}
ll qmul(ll a, ll b, ll P) 
{
  	ll L = a * (b >> 25LL) % P * (1LL << 25) % P;
  	ll R = a * (b & ((1LL << 25) - 1)) % P;
  	return (L + R) % P;
}
ll qpow(ll a,ll b,ll p)
{
	ll res = 1 % p;
	while(b)
	{
		if(b & 1)	res = qmul(res,a,p);
		a = qmul(a,a,p);
		b >>= 1;
	}
	return res;
}
int main()
{
	memset(ver,-1,sizeof ver);
	int n,k;scanf("%d%d",&n,&k);
	for(int i = 1;i < n;++i)
	{
		int u,v,c;scanf("%d%d%d",&u,&v,&c);
		add(u,v,c);add(v,u,c);
	}
	int totid = 0;
	for(int i = 1;i <= n;++i)
		if(!id[i])
		{
			id[i] = ++totid;
			dfs(i);
		}
	for(int i = 1;i <= n;++i)
		++cnt[id[i]];
	ll res = 0;
	for(int i = 1;i <= totid;++i)
		res = (res + qpow(cnt[i],k,MOD)) % MOD;
	res = ((qpow(n,k,MOD) - res) % MOD + MOD) % MOD;
    printf("%lld",res);
    return 0;
}
posted @ 2020-08-11 14:49  随处可见的阿宅  阅读(103)  评论(0编辑  收藏  举报