2020 CCPC Wannafly Winter Camp Day7

K
发现即是求 k ∗ ( k + 1 ) / 2 > = m a x ( 0 , b 1 − k ∗ a 1 ) + m a x ( 0 , b 2 − k ∗ a 2 ) k*(k+1)/2>=max(0, b1-k*a1)+max(0,b2-k*a2) k(k+1)/2>=max(0,b1ka1)+max(0,b2ka2)
单调函数,求出最小满足的k二分即可

#include 
using namespace std;
const int maxn = 1e3 + 5;
typedef long long ll;
ll a1, a2, b1, b2;
int main()
{
    scanf("%lld%lld", &a1, &a2);
    int n; scanf("%d", &n);
    ll ans = 1e8;
    for (int i = 0; i < n; ++i) {
    	scanf("%lld%lld", &b1, &b2);

        ll l = 1, r = 1e8;
    	while (l < r) {
    		ll mid = (l + r) / 2;

    		if (mid * (mid + 1) >= 2 * (max(b1 - mid * a1, 0ll) + max(b2 - mid * a2, 0ll)))
    			r = mid;
    		else
    			l = mid + 1;
    	}
    	ans = min(ans, l);
    }
    printf("%lld\n", ans);
}

H
每轮选择两个数字的概率为 1 n ∗ ( n − 1 ) 1\over n*(n-1) n(n1)1,考虑顺序还需要乘2,则我们处理出每个数字互质的欧拉函数,求出欧拉函数和sum
则答案即为 1 n ∗ ( n − 1 ) ∗ 2 ∗ ( n / 2 ) ∗ s u m {1\over n*(n-1)}*2*(n/2)*sum n(n1)12(n/2)sum

#include 
using namespace std;
typedef long long ll;
const int maxn = 5000 + 5;
vector prime;
int vis[maxn], phi[maxn];
typedef long long ll;
void get() {
	phi[1] = 1;
	for (int i = 2; i < maxn; ++i) {
		if (!vis[i]) {
			prime.push_back(i);
			phi[i] = i - 1;
		}
		for (int j = 0; j < prime.size() && i * prime[j] < maxn; ++j) {
			if (i % prime[j] == 0) {
				vis[i * prime[j]] = 1;
				phi[i * prime[j]] = phi[i] * prime[j];
			}
			else {
				vis[i * prime[j]] = 1;
				phi[i * prime[j]] = phi[i] * (prime[j] - 1);
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);

	get();
	int n; cin >> n;
	if (n == 1) {
		cout << "0/1" << endl;
		return 0;
	}
	ll ans = 0, rot = 0;
	for (int i = 2; i <= n; ++i)
		ans += phi[i];
	rot = 1ll * n * (n - 1);
	ans *= 2ll * (n / 2);
	ll gcd = __gcd(ans, rot);
	cout << ans	/ gcd << '/' << rot / gcd << endl;
}

G
注意到n和m很小,且发现走完每个格子后的格子都是 n ∗ m n*m nm的贡献
所以暴力求出便利所有格子的最大贡献

#include 
using namespace std;
const int maxn = 12 + 2;
typedef long long ll;

int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
int a[maxn][maxn];
int n, m, s, t; ll k, K; 
ll solve(int x, int y, ll kk) {
	if (kk == K)
		return a[x][y];
	ll rhs = 0; int tmp = a[x][y];
	a[x][y] = 0;
	for (int i = 0; i < 4; ++i) {
		int xx = x + dir[i][0];
		int yy = y + dir[i][1];
		if (xx <= 0 || xx > n)
			continue;
		if (yy <= 0 || yy > m)
			continue;
		rhs = max(rhs, solve(xx, yy, kk + 1));
	}
	a[x][y] = tmp;
	return tmp + rhs;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> n >> m >> s >> t >> k;
	K = min(k, 1ll * n * m);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j)
			cin >> a[i][j];
	}
	
	ll ans = solve(s, t, 1) + K * (K - 1) / 2 + n * m * (k - K);
	cout << ans << endl;
}

L
只需要考虑每个点的存在入边的点,用二进制保存其他点是否到该点存在入边关系
用二进制保存所有点的黑白关系记为状态,则当前状态与该点的边关系的和(&)后二进制串中的1的数量决定了该点下一个颜色
考虑最多 2 20 2^{20} 220个状态,会出现环;我们处理出环的开始端点和长度;
前缀和处理未到环的路径能形成的黑色点个数;前缀和处理一个环能形成的黑色点个数;二分求出最小满足情况

#include 
using namespace std;
const int maxn = 20 + 2;
const int maxm = 1 << maxn;
typedef long long ll;

int g[maxn], vis[maxm], b[maxm], cnt;
int c0[maxm][maxn], c1[maxm][maxn];
int main()
{
    ios::sync_with_stdio(false); cin.tie(0);

    int n, m, q; cin >> n >> m >> q;
    int s = 0;
    for (int i = 0; i < n; ++i) {
        int x; cin >> x;
        s |= x << i;
    }
    for (int i = 0; i < m; ++i) {
        int u, v; cin >> u >> v; u--, v--;
        g[v] |= 1 << u;
    }
    memset(vis, -1, sizeof(vis));
    vis[s] = 0; b[0] = s;
    int st, loop;
    while (true)
    {
        int ns = 0;
        for (int i = 0; i < n; ++i) {
            ns |= (__builtin_parity(g[i] & s) << i);
        }
        b[++cnt] = s = ns;
        if (~vis[s]) {
            st = vis[s];
            loop = cnt - st;
            break;
        }
        vis[s] = cnt;
    }

    for (int i = 0; i <= st; ++i)
        for (int j = 0; j < n; ++j)
            c0[i][j] = (b[i] >> j & 1);
    for (int i = 1; i <= st; ++i)
        for (int j = 0; j < n; ++j)
            c0[i][j] += c0[i - 1][j];
    for (int i = 1; i <= loop; ++i)
        for (int j = 0; j < n; ++j)
            c1[i][j] = c1[i - 1][j] + (b[i + st] >> j & 1);
    
    int x; ll l, r, mid, k;
    while (q--) {
        cin >> x >> k; x--;
        if (k <= c0[st][x]) {
            //cout << "case 1" << endl;
            l = 0, r = st;
            while (l != r) {
                mid = (l + r) >> 1;
                if (c0[mid][x] < k)
                    l = mid + 1;
                else
                    r = mid;
            }
            cout << l << endl;
        }
        else {
            //cout << "case 2 " << st << endl;
            k -= c0[st][x];
            ll ans = st;
            if (c1[loop][x] == 0)
                cout << -1 << endl;
            else {
                if (k >= c1[loop][x]) {
                    if (k % c1[loop][x]) {
                        ans += k / c1[loop][x] * loop;
                        k %= c1[loop][x];
                    }
                    else {
                        ans += (k / c1[loop][x] - 1) * loop;
                        k = c1[loop][x];
                    }
                }
                l = 1, r = loop;
                while (l != r) {
                    mid = (l + r) >> 1;
                    if (c1[mid][x] < k)
                        l = mid + 1;
                    else
                        r = mid;
                }
                cout << ans + l << endl;
            }
        }
    }
}

A
考虑对答案的影响因素是 k k k的大小,且每对左右端点 ( i , j ) (i,j) (i,j)对在 a [ i ] < k < a [ j ] a[i]a[i]<k<a[j]贡献为 2 n − 1 − i − j = 2 n − j ∗ 2 i − 1 2^{n-1-i-j}=2^{n-j}*2^{i-1} 2n1ij=2nj2i1
a n s k ans_k ansk为k时的答案,则对 a n s k + 1 ans_{k+1} ansk+1,我们需要减掉只对 k k k产生贡献的答案和加上只对 k + 1 k+1 k+1产生贡献的答案
我们计算 δ k = a n s k − a n s k − 1 \delta k =ans_k-ans_{k-1} δk=anskansk1
只对 k k k产生贡献的情况为 ( 1 , k + 1 ) , ( 2 , k + 1 ) , . . . , ( k − 1 , k + 1 ) (1,k+1),(2,k+1),...,(k-1,k+1) (1,k+1),(2,k+1),...,(k1,k+1)
只对 k + 1 k+1 k+1产生贡献的情况为 ( k , k + 2 ) , ( k , k + 3 ) , . . . , ( k , n ) (k,k+2),(k,k+3),...,(k,n) (k,k+2),(k,k+3),...,(k,n)
因此正倒遍历序列,将每个数x作为右端点i,则计算小于x的贡献为sum,则对 δ x \delta x δx产生 − s u m ∗ 2 n − i -sum*2^{n-i} sum2ni,计算该数作为左端点i,则计算大于x的贡献为sum,则对 δ ( x + 1 ) \delta (x+1) δ(x+1)产生 s u m ∗ 2 n − i sum*2^{n-i} sum2ni

#include 
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
const ll mod = 1e9 + 7;
ll sub(ll a, ll b) {
    return a - b < 0 ? a - b + mod : a - b;
}
ll add(ll a, ll b) {
    return a + b >= mod ? a + b - mod : a + b;
}
ll tr[maxn << 1];
int lowbit(int x) {
    return x & -x;
}
void update(int pos, int val, int n) {
    for (int i = pos; i <= n; i += lowbit(i)) {
        tr[i] = add(tr[i], val);
    }
}
ll sum(int pos) {
    ll rhs = 0;
    for (int i = pos; i; i -= lowbit(i))
        rhs = add(rhs, tr[i]);
    return rhs;
}
ll mul[maxn], delta[maxn];
int a[maxn];
int main()
{
    ios::sync_with_stdio(false); cin.tie(0);
    int n; cin >> n;
    mul[0] = 1;
    for (int i = 1; i <= n; ++i) {
        mul[i] = mul[i - 1] * 2 % mod;
        cin >> a[i];
    }
    for (int i = 1; i <= n; ++i) {
        ll val = sum(a[i] - 1) *  mul[n - i] % mod;
        delta[a[i]] = sub(delta[a[i]], val);
        val = sub(sum(n), sum(a[i])) * mul[n - i] % mod;
        delta[a[i] + 1] = add(delta[a[i] + 1], val);
        update(a[i], mul[i - 1], n);
    }
    for (int i = 1; i <= (n << 1); ++i)
        tr[i] = 0;
    for (int i = n; i >= 1; --i) {
        ll val = sub(sum(n), sum(a[i])) * mul[i - 1] % mod;
        delta[a[i] + 1] = add(delta[a[i] + 1], val);
        val = sum(a[i] - 1) * mul[i - 1] % mod;
        delta[a[i]] = sub(delta[a[i]], val);
        update(a[i], mul[n - i], n);
    }
    for (int i = 1; i <= n; ++i) {
        delta[i] = add(delta[i - 1], delta[i]);
        cout << delta[i] << '\n';
    }
}

你可能感兴趣的:(总结)