闲话
这场的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;
}