1. 程式人生 > >Codeforces Round #447 (Div. 2) 題解

Codeforces Round #447 (Div. 2) 題解

強連通分量 統計 題解 urn spa 最大 ret num 所有

Problem A

直接暴力,當然我O(n)過的。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;


int ans = 0;
char s[205];
int a[205], b[205];
int n;


int main(){


	scanf("%s", s + 1);
	n = strlen(s + 1);

	rep(i, 1, n) if (s[i] == ‘Q‘) a[i] = a[i - 1] + 1;
	else a[i] = a[i - 1];

	dec(i, n, 1) if (s[i] == ‘Q‘) b[i] = b[i + 1] + 1;
	else b[i] = b[i + 1];

	rep(i, 1, n) if (s[i] == ‘A‘) ans += a[i] * b[i];

	printf("%d\n", ans);


	return 0;
}

  

Problem B

打表找規律……當k為-1且n和m奇偶性不同的時候答案為0。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;

const LL mod = 1e9 + 7;

LL n, m;
int k;

inline LL Pow(LL a, LL b, LL Mod){
        LL ret(1);
        for (; b; b >>= 1, (a *= a) %= Mod) if (b & 1) (ret *= a) %= Mod;
        return ret;
}	


int main(){

	scanf("%lld%lld%d", &n, &m, &k);
	if (k == 1){
		LL yy = n - 1;
		LL hh = Pow(2, yy, mod);
		LL ans = Pow(hh, m - 1, mod);
		printf("%lld\n", ans);
	}

	else{
		LL tt = n + m;
		if (tt % 2 == 1) return 0 * puts("0");
		if (n % 2 == 1){
			LL oo = (n + 1) / 2;
			LL pp = oo - 1;
			LL mul = Pow(16, pp, mod);
			LL mm = m / 2;
			LL ans = Pow(mul, mm, mod);
			printf("%lld\n", ans);
		}

		else{
			LL xx = n / 2, yy = m / 2;
			LL mul = 4 * Pow(16, xx - 1, mod) % mod;
			LL ff = 2 * Pow(4, xx - 1, mod) % mod;
			LL ans = ff * Pow(mul, yy - 1, mod) % mod;
			printf("%lld\n", ans);
		}

	}		


	return 0;
}

  

Problem C

構造題(賽時被X……)

若最大的那個不能整除所有比他小的數,那麽無解

否則就按照s1,s1,s2,s1,s3,s1,s4,s1,...sn,s1這麽輸出。

關鍵是想到。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, f)	for (int i(a); i <= (f); ++i)
#define dec(i, a, f)	for (int i(a); i >= (f); --i)
#define MP		make_pair
#define fi		first
#define se		second

const int N = 1e6;


int n;
int c[10100];
int f[N];

int main(){

	scanf("%d", &n);
	rep(i, 1, n) scanf("%d", c + i);
	rep(i, 2, n) if (c[i] % c[1]) return 0 * puts("-1");
	printf("%d\n", n << 1);
	rep(i, 1, n) printf("%d %d\n", c[1], c[i]);
	putchar(10);
}

  

Problem D

暴力存下所有點的所有兒子到他的距離。

因為這是完全二叉樹所以內存可以承受。

空間開銷$nlogn$,時間復雜度$O(nlog^{2}n)$,如果用歸並排序的話那就$O(nlogn)$

然後查詢的時候一步步往上爬,把符合條件的答案統計進來。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second

typedef long long LL;
typedef pair <int, LL> PII;

const int N = 1e6 + 10;

int n, m;
vector <LL > g[N], s[N];
LL l[N], ans = 0;

inline int fa(int x){ return x >> 1;}
inline int ls(int x){ return x << 1;}
inline int rs(int x){ return x << 1 | 1;}

PII calc(int x, LL len){
	if (len < 0) return MP(0, 0);
	if (x > n) return MP(0, 0);
	int sz = (int)g[x].size();
	if (g[x][sz - 1] <= len) return MP(sz, s[x][sz - 1]);

	int l = 0, r = sz - 1;
	while (l + 1 < r){
		int mid = (l + r) >> 1;
		if (g[x][mid] <= len) l = mid;
		else r = mid - 1;
	}

	int t;
	if (g[x][r] <= len) t = r; else t = l;
	return MP(t + 1, s[x][t]);	

}

void dfs(int x){
	if (ls(x) <= n){
		dfs(ls(x));
		for (auto u : g[ls(x)]) g[x].push_back(l[ls(x)] + u);
	}

	if (rs(x) <= n){
		dfs(rs(x));
		for (auto u : g[rs(x)]) g[x].push_back(l[rs(x)] + u);

	}

	g[x].push_back(0);
	sort(g[x].begin(), g[x].end());
}

int main(){

	scanf("%d%d", &n, &m);
	rep(i, 2, n) scanf("%lld", l + i);

	dfs(1);

	rep(i, 1, n){
		int sz = g[i].size();
		s[i].resize(sz + 2);
		s[0] = g[0];
		rep(j, 1, sz - 1) s[i][j] = s[i][j - 1] + g[i][j];
	}

	while (m--){
		int x, y;
		LL len, Len;
		scanf("%d%lld", &x, &len);
		Len = len;
		PII cnt = calc(x, len);
		ans = 0;
		ans = cnt.fi * len - cnt.se;
		y = x, x = fa(x);
		len -= l[y];
		while (x && len >= 0){
			if (ls(x) == y){
				cnt = calc(rs(x), len - l[rs(x)]);
				ans += cnt.fi * Len - cnt.se - cnt.fi * (Len - len + l[rs(x)]);
			}

			else{
				cnt = calc(ls(x), len - l[ls(x)]);
				ans += cnt.fi * Len - cnt.se - cnt.fi * (Len - len + l[ls(x)]);
			}

			ans += len;
			y = x;
			x = fa(x);
			len -= l[y];
		}
		printf("%lld\n", ans);
	}

	return 0;
}

  

Problem E

預處理出所有強連通分量之後縮點。

縮成的點的權值為該點代表的強連通分量的所有邊的價值和(根據題目算出來)

然後就變成了一個DAG,DP一下就可以了。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second

typedef long long LL;

const int N = 1e6 + 10;

vector <pair <int, int > > v[N], g[N];
stack <int> stk;
LL f[N], val[N];
int n, m;
int low[N], dfn[N], sccno[N];
int num = 0, ti = 0;
int s;

void dfs(int x){
	low[x] = dfn[x] = ++ti;
	stk.push(x);
	for (auto edge : v[x]){
		int u = edge.fi, w = edge.se;
		if (!low[u]){
			dfs(u);
			dfn[x] = min(dfn[x], dfn[u]);
		}

		else if (!sccno[u]) dfn[x] = min(dfn[x], low[u]);
	}

	if (dfn[x] == low[x]){
		++num;
		while (true){
			int u = stk.top();
			stk.pop();
			sccno[u] = num;
			if (u == x) break;
		}
	}
}

LL work(int x){
	if (~f[x]) return f[x];
	f[x] = 0;
	for (auto edge : g[x]) f[x] = max(f[x], work(edge.fi) + edge.se);
	return f[x] += val[x];
}

LL calc(int x){
	int rt = sqrt(2 * x);
	while ((LL)rt * (rt - 1) / 2 <= x) rt++;
	while ((LL)rt * (rt - 1) / 2 >  x) rt--;
	return (LL)rt * x - (LL)(rt + 1) * rt * (rt - 1) / 6;
}

int main(){

	scanf("%d%d", &n, &m);
	rep(i, 1, m){
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		v[x].push_back(MP(y, z));
	}

	scanf("%d", &s);
	dfs(s);

	rep(i, 1, n){
		for (auto edge : v[i]){
			int x = i, y = edge.fi;
			if (sccno[x] == sccno[y]) val[sccno[x]] += calc(edge.se);
			else g[sccno[x]].push_back(MP(sccno[y], edge.se));
		}
	}

	memset(f, -1, sizeof f);
	printf("%lld\n", work(sccno[s]));
	return 0;
}

  

Codeforces Round #447 (Div. 2) 題解