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


    水题大赛

    A. Superhero Transformation

    1S 256MB

    题意

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

    变化次数不限:

    • 元音字母之间可以互相转换,如au,oaa o u,o o a,但是a̸ba o ot b
    • 辅音字母之间可以互相转换。如byb o y,但是b̸ab o ot 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
    ", ans);
    }
    

    C. Creative Snap

    1S 256MB

    题意

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

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

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

    分析

    按题意递归即可,单位长度区间最多被访问kk次,深度为nn,再加上里面有个,总时间复杂度即为O(nklogk)O(nkcdot 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
    ", solve(1, 1<<n, 1, k));
    }
    

    D. Destroy the Colony

    2S 512MB

    题意

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

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

    举个栗子,s=aabbcbcolor{black}s=color{blue}acolor{black}abbcolor{blue}ccolor{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的博客
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    由于每次只用dpdpn2frac n2的值,再加上是无序字符对,(A,B)(B,A)(A,B)(B,A)只用算一次。那么实际上复杂度为nU22=132600000ncdot frac{|U|^2}2=132600000。而且因为只要A,BA,B的出现次数加起来超过n2frac 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
    ", 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,rleft(1le k,rle n,mle min(k,300),sum kle10^5 ight)。表示询问在以rr为根的情况下,把给出kk个点,分成至多mm组的分配方案数,使得没有一个点和它的祖先在同一个组内。答案膜109+710^9+7输出。

    分析

    看到k105sum kle10^5mmin(k,300)mle 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
    ", 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);
    	}
    }
    
  • 相关阅读:
    hive 查询结果导入到hdfs中 row format 报错
    每日一python
    关于setuptools的版本问题
    数据库的索引
    hadoop5--mapreduce设计模式
    hadoop4--深入mapreduce
    hadoop3--编写简单的map reduce
    hadoop学习2----HDFS操作
    Git-5
    鸟哥的linux私房菜学习笔记 ---第8章-1
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039275.html
Copyright © 2020-2023  润新知