水题大赛
A. Superhero Transformation
1S 256MB
题意
给出两个小写字母串,询问是否可以把串通过变化转成串。字符串长度最大为。
变化次数不限:
- 元音字母之间可以互相转换,如,但是
- 辅音字母之间可以互相转换。如,但是
分析
判断长度是否相等,在判断每个位置上的数是否同为元音或者辅音。
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
题意
有个正整数,次操作。每次操作可以选择两种方式:
- 一:给某个数加上
- 二:删去某个数
要让平均值最大。有一个限制,作用在一个位置上的操作次数不超过。
分析
按照贪心的策略,要删肯定从最小的开始删。那么排序后枚举删了几个数,把剩下的操作数加在剩下个位置上就行了,的限制就取一个就行了。
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
", ans);
}
C. Creative Snap
1S 256MB
题意
你有一个长度为的序列,有个位置上有人,题目给出。不同的人可能在相同的位置。你要控制整个序列,控制一个序列有两种方式:
- 一:如果这个区间里面没有人,可以花费代价控制它;否则如果这个区间有个人,区间长度为,可以花费控制它。(题目给出)
- 二:分别控制左半区间和右半区间。
要让控制整个序列的代价最小
分析
按题意递归即可,单位长度区间最多被访问次,深度为,再加上里面有个,总时间复杂度即为(不满)
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
", solve(1, 1<<n, 1, k));
}
D. Destroy the Colony
2S 512MB
题意
一个字符串为偶数由大小写字母组成,你可以将它重新排列,一个合法的排列方案必须满足相同字符(区分大小写)全部都在左半边或者右半边,即同一侧。
有次询问,每次询问,表示两个位置,记这两个位置上的字符分别为(可能等于)。你需要输出 满足所有的和字符都在同一侧 的合法排序数。
举个栗子,。如果,那么所有的都要在同一侧,并且需要所有的在同一侧。合法的方案就有
“aacbbb”,“bbbcaa”
“acabbb”,“bbbaca”
“caabbb”,“bbbaac”
分析
以下分析转自 Inspector_Javert的博客
由于每次只用出的值,再加上是无序字符对,只用算一次。那么实际上复杂度为。而且因为只要的出现次数加起来超过就直接输出,记忆化的话不一定把每个无序数对都做,于是就能过了。
#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
", ans[x][y]);
}
}
E. Tree
1.5S 256MB
题意
给出一棵树,节点为,有次询问。
每次询问格式为。表示询问在以为根的情况下,把给出的个点,分成至多组的分配方案数,使得没有一个点和它的祖先在同一个组内。答案膜输出。
分析
看到和,那么的算法一定是可行的。这样的分组方案数一般就是,但是由于是给出的点不方便树形,我们就直接按顺序。
定义表示给定的个数中在为根的情况下是的祖先的点的数量。那么一定成立。所以我们按排序之后就可以只用考虑当前点不能祖先放在一起,而没有后效性了。
定义表示把前个点分成了组的方案数,由于我们知道的祖先们肯定两两不在同一个组内,那么就可以这么写转移方程式:
我们将倒序枚举就能够去掉第一维了。
如何求?其实就是到的路径上有效点的数量,这个用维护就行了。不过还是要写个。
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
", 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
", j, dp[j]);
printf("%d
", ans);
}
}