CodeCraft-19 and Codeforces Round #537 (Div. 2) 题解

水题大赛

A. Superhero Transformation

1S 256MB

题意

给出两个小写字母串a,ba,b,询问是否可以把aa串通过变化转成bb串。字符串长度最大为10001000

变化次数不限:

  • 元音字母之间可以互相转换,如au,oaa\to u,o\to a,但是a̸ba\to\not b
  • 辅音字母之间可以互相转换。如byb\to y,但是b̸ab\to\not a

分析

判断长度是否相等,在判断每个位置上的数是否同为元音或者辅音。

CODE

#include <bits/stdc++.h>
using namespace std;
char a[1005], b[1005];
int n, m;
inline bool chk(char c) {
	return c == 'a' || c == 'e' || c == 'i' || c == 'u' || c == 'o';
}
int main () {
	scanf("%s%s", a+1, b+1);
	n = strlen(a+1);
	m = strlen(b+1);
	if(n != m) return puts("No"), 0;
	for(int i = 1; i <= n; ++i)
		if(chk(a[i])^chk(b[i])) return puts("No"), 0;
	puts("Yes");
}

B. Average Superhero Gang Power

1S 256MB

题意

n(105)n(\le 10^5)个正整数ai(106)a_i(\le10^6)m(107)m(\le 10^7)次操作。每次操作可以选择两种方式:

  • 一:给某个数加上11
  • 二:删去某个数

要让平均值最大。有一个限制,作用在一个位置上的操作次数不超过k(105)k(\le 10^5)

分析

按照贪心的策略,要删肯定从最小的开始删。那么排序后枚举删了几个数ii,把剩下的操作数加在剩下nin-i个位置上就行了,kk的限制就取一个minmin就行了。

CODE

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, k, m;
double a[MAXN];
int main () {
	scanf("%d%d%d", &n, &k, &m);
	for(int i = 1; i <= n; ++i)
		scanf("%lf", &a[i]);
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= n; ++i) a[i] += a[i-1];
	double ans = a[n] / n;
	for(int i = 0; i < n && i <= m; ++i) {
		double add = min(1.0*m-i, 1.0*k*(n-i));
		ans = max(ans, (a[n]-a[i]+add)/(n-i));
	}
	printf("%.10f\n", ans);
}

C. Creative Snap

1S 256MB

题意

你有一个长度为2n(1n30)2^n(1\le n\le 30)的序列,有k(1k105)k(1\le k\le10^5)个位置上有人,题目给出。不同的人可能在相同的位置。你要控制整个序列,控制一个序列有两种方式:

  • 一:如果这个区间里面没有人,可以花费AA代价控制它;否则如果这个区间有kk个人,区间长度为ll,可以花费BklB\cdot k\cdot l控制它。(A,BA,B题目给出(1A,B104)(1\le A,B\le 10^4)
  • 二:分别控制左半区间和右半区间。

要让控制整个序列的代价最小

分析

按题意递归即可,单位长度区间最多被访问kk次,深度为nn,再加上里面有个,总时间复杂度即为O(nklogk)O(nk\cdot logk)(不满)

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 100005;
int n, k, a[MAXN], A, B;
LL solve(int l, int r, int x, int y) {
	if(x > y) return A;
	LL now = 1ll * B * (y-x+1) * (r-l+1);
	if(l == r) return now;
	int mid = (l + r) >> 1;
	int pos = upper_bound(a + x, a + y + 1, mid) - a;
	return min(now, solve(l, mid, x, pos-1) + solve(mid+1, r, pos, y));
}
int main () {
	scanf("%d%d%d%d", &n, &k, &A, &B);
	for(int i = 1; i <= k; ++i)
		scanf("%d", &a[i]);
	sort(a + 1, a + k + 1);
	printf("%I64d\n", solve(1, 1<<n, 1, k));
}

D. Destroy the Colony

2S 512MB

题意

一个字符串s(2s105 &amp;&amp; ss(2\le|s|\le10^5\ \&amp;\&amp;\ |s|为偶数))由大小写字母组成,你可以将它重新排列,一个合法的排列方案必须满足相同字符(区分大小写)全部都在左半边(1s/2)(1\to|s|/2)或者右半边(s/2+1s)(|s|/2+1\to|s|),即同一侧。

q(105)q(\le 10^5)次询问,每次询问x,y(x≠y)x,y(x=\not y),表示两个位置,记这两个位置上的字符分别为A,BA,BAA可能等于BB)。你需要输出 满足所有的AABB字符都在同一侧合法排序数

举个栗子,s=aabbcb\color{black}s=\color{blue}a\color{black}abb\color{blue}c\color{black}b。如果(x,y)=(1,5)(x,y)=(1,5),那么所有的a,c&#x27;a&#x27;,&#x27;c&#x27;都要在同一侧,并且需要所有的b&#x27;b&#x27;在同一侧。合法的方案就有
“aacbbb”,“bbbcaa”
“acabbb”,“bbbaca”
“caabbb”,“bbbaac”

分析

以下分析转自 Inspector_Javert的博客
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于每次只用dpdpn2\frac n2的值,再加上是无序字符对,(A,B)(B,A)(A,B)(B,A)只用算一次。那么实际上复杂度为nU22=132600000n\cdot \frac{|U|^2}2=132600000。而且因为只要A,BA,B的出现次数加起来超过n2\frac n2就直接输出00,记忆化的话不一定把每个无序数对都做dpdp,于是就能过了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int mod = 1e9 + 7;
char s[MAXN];
int n, m, c = 52, a[MAXN], cnt[55];
inline int num(char x) {
    return (x >= 'a' && x <= 'z') ? x-'a'+1 : x-'A'+27;
}
int fac[MAXN>>1], inv[MAXN>>1];
inline void pre(int N) {
    fac[0] = inv[0] = fac[1] = inv[1] = 1;
    for(int i = 2; i <= N; ++i)
        fac[i] = 1ll * fac[i-1] * i % mod,
        inv[i] = 1ll * (mod - mod/i) * inv[mod%i] % mod;
    for(int i = 2; i <= N; ++i)
        inv[i] = 1ll * inv[i] * inv[i-1] % mod;
}
int ans[55][55], f[MAXN>>1], g[MAXN>>1], h[MAXN>>1];
int main () {
	scanf("%s%d", s+1, &m);
	n = strlen(s+1); pre(n/2);
	for(int i = 1; i <= n; ++i)
        ++cnt[a[i] = num(s[i])];

    int base = 1ll * fac[n/2] * fac[n/2] % mod;
    for(int i = 1; i <= 52; ++i) if(cnt[i])
        base = 1ll * base * inv[cnt[i]] % mod;

    f[0] = 1;
    for(int i = 1; i <= 52; ++i) if(cnt[i])
        for(int j = n/2; j >= cnt[i]; --j)
            f[j] = (f[j] + f[j-cnt[i]]) % mod;

    memset(ans, -1, sizeof ans);
    int x, y;
    while(m--) {
        scanf("%d%d", &x, &y);
        x = a[x], y = a[y];
        if(x > y) swap(x, y);
        if(!(~ans[x][y])) {
            int A = cnt[x], B = cnt[y];
            if(x == y) ans[x][y] = 1ll * base * f[n/2] % mod;
            else {
                if(A + B > n/2) ans[x][y] = 0;
                else {
                    for(int j = 0; j <= n/2; ++j)
                        g[j] = (f[j] - ((j >= A) ? g[j-A]: 0) + mod) % mod;
                    for(int j = 0; j <= n/2; ++j)
                        h[j] = (g[j] - ((j >= B) ? h[j-B]: 0) + mod) % mod;
                    ans[x][y] = 2ll * base * h[n/2] % mod;
                }
            }
        }
        printf("%d\n", ans[x][y]);
    }
}

E. Tree

1.5S 256MB

题意

给出一棵树,节点为n(105)n(\le 10^5),有q(105)q(\le 10^5)次询问。

每次询问格式为k,m,r(1k,rn,mmin(k,300),k105)k,m,r\left(1\le k,r\le n,m\le min(k,300),\sum k\le10^5\right)。表示询问在以rr为根的情况下,把给出kk个点,分成至多mm组的分配方案数,使得没有一个点和它的祖先在同一个组内。答案膜109+710^9+7输出。

分析

看到k105\sum k\le10^5mmin(k,300)m\le min(k,300),那么O(km)O(km)的算法一定是可行的。这样的分组方案数一般就是dpdp,但是由于是给出的点不方便树形dpdp,我们就直接按顺序dpdp

定义f[i]f[i]表示给定的kk个数中在rr为根的情况下是ii的祖先的点的数量。那么f[ancestor(i)]&lt;f[i]f[ancestor(i)]&lt;f[i]一定成立。所以我们按f[i]f[i]排序之后就可以只用考虑当前点不能祖先放在一起,而没有后效性了。

定义dp[i][j]dp[i][j]表示把前ii个点分成了jj组的方案数,由于我们知道ii的祖先们肯定两两不在同一个组内,那么就可以这么写转移方程式:
dp[i][j]=dp[i1][j](jf[i])+dp[i1][j1]dp[i][j]=dp[i-1][j]\cdot (j-f[i])+dp[i-1][j-1]我们将jj倒序枚举就能够去掉第一维了。

如何求f[i]f[i]f[i]f[i]其实就是iirr的路径上有效点的数量,这个用BITBIT维护就行了。不过还是要写个lcalca

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 100005;
const int mod = 1e9 + 7;
const int MAXM = 305;
int n, q, k, m, r, f[MAXN], a[MAXN], dp[MAXM];
int dep[MAXN], dfn[MAXN], tmr;
int son[MAXN], fa[MAXN], top[MAXN], sz[MAXN];
vector<int>G[MAXN];
void dfs(int u, int ff) {
	dep[u] = dep[fa[u] = ff] + (sz[u] = 1);
	for(auto v : G[u])
		if(v != ff) {
			dfs(v, u), sz[u] += sz[v];
			if(sz[v] > sz[son[u]])
				son[u] = v;
		}
}
void dfs2(int u, int tp) {
	top[u] = tp; dfn[u] = ++tmr;
	if(son[u]) dfs2(son[u], tp);
	for(auto v : G[u])
		if(v != fa[u] && v != son[u])
			dfs2(v, v);
}
inline int lca(int u, int v) {
	while(top[u]^top[v]) {
		if(dep[top[u]] > dep[top[v]]) u = fa[top[u]];
		else v = fa[top[v]];
	}
	return dep[u] > dep[v] ? v : u;
}
int T[MAXN]; bool vis[MAXN];
inline void upd(int x, int val) {
	while(x <= n) T[x] += val, x += x&-x;
}
inline int qsum(int x) { int re = 0;
	while(x) re += T[x], x -= x&-x;
	return re;
}
inline bool cmp(int A, int B) { return f[A] < f[B]; }
int main () {
	scanf("%d%d", &n, &q);
	for(int i = 1, x, y; i < n; ++i)
		scanf("%d%d", &x, &y),
		G[x].push_back(y),
		G[y].push_back(x);
	dfs(1, 0); dfs2(1, 1);
	while(q--) {
		scanf("%d%d%d", &k, &m, &r);
		for(int i = 1; i <= k; ++i) {
			scanf("%d", &a[i]);
			int v = a[i];
			upd(dfn[v], 1), upd(dfn[v]+sz[v], -1), vis[v] = 1;
		}
		int qsumr = qsum(dfn[r]);
		for(int i = 1; i <= k; ++i) {
			int v = a[i], LCA = lca(r, v);
			f[v] = qsum(dfn[v]) + qsumr - 2*qsum(dfn[LCA]) + vis[LCA] - 1;
			//printf("f[%d] = %d  lca(r:%d, v:%d) = %d\n", v, f[v], r, v, LCA);
		}
		for(int i = 1; i <= k; ++i) {
			int v = a[i];
			upd(dfn[v], -1), upd(dfn[v]+sz[v], 1), vis[v] = 0;
		}
		sort(a + 1, a + k + 1, cmp);
		for(int j = 0; j <= m; ++j) dp[j] = 0;
		dp[0] = 1;
		for(int i = 1; i <= k; ++i) {
			int v = a[i];
			for(int j = min(i, m); j; --j) {
				if(j <= f[v]) dp[j] = 0;
				else dp[j] = (1ll * dp[j] * (j-f[v]) % mod + dp[j-1]) % mod;
			}
			dp[0] = 0;
		}
		int ans = 0;
		for(int j = 1; j <= m; ++j)
			ans = (ans + dp[j]) % mod;//, printf("dp[%d] = %d\n", j, dp[j]);
		printf("%d\n", ans);
	}
}
posted @ 2019-12-14 14:50  _Ark  阅读(159)  评论(0编辑  收藏  举报