[2018.12.4]Codeforces round #525题解

时隔一个月再一次打一场比赛,之前两场都FST了。

1088D - Ehab and another another xor problem

Ehab plays a game with Laggy. Ehab has 2 hidden integers (?,?). Laggy can ask a pair of integers (?,?) and Ehab will reply with:

1 if ?⊕?>?⊕?.
0 if ?⊕?=?⊕?.
-1 if ?⊕?<?⊕?.
Operation ?⊕? is the bitwise-xor operation of two numbers ? and ?.

Laggy should guess (?,?) with at most 62 questions. You’ll play this game. You’re Laggy and the interactor is Ehab.

It’s guaranteed that 0≤?,?< 2 30 2^{30} 230.
感觉这道题不可做
先查询(0,0)确定a,b的大小关系
从高到低按位查询,每次查询(a xor 2 i 2^i 2i,b)和(a, b ^ 2 i 2^i 2i)
如果答案相同的话说明这两位不一样不妨将a加上这个值
如果不同的话说明这两位一样。
感性理解一下

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define REP(i, a, b) for(int i = a; i <= b; i++)
#define PER(i, a, b) for(int i = a; i >= b; i--)
#define LL long long
inline int read(){
    int x = 0, flag = 1;char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') flag = - 1;
        ch = getchar();
    }
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return x * flag;
}

int ask(int c, int d) {
	cout << "? " << c << " " << d << endl;
	int ans;
	cin >> ans;
	return ans;
}

int main() {   
	cout.flush();
	int a = 0, b = 0, big = ask(0, 0);
	for(int i = 29; i >= 0; --i) {
		int f = ask(a ^ (1 << i), b), s = ask(a, b ^ (1 << i));
		if(f == s) {
			if(big == 1) a ^= (1 << i);
			else b ^= (1 << i);
			big = f;
		} else if(f == -1) {
			a ^= (1 << i);
			b ^= (1 << i);
		}
	}
	cout << "! " << a << " " << b << endl;
	return 0;
}

1088E - Ehab and a component choosing problem

从树上选择k条不相交的路径记它们的点权和为S,要求最大化S/K(k也尽量大)
树形DP,显然如果都为正的话只选最大的显然最优

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define REP(i, a, b) for(int i = a; i <= b; i++)
#define PER(i, a, b) for(int i = a; i >= b; i--)
#define LL long long
inline int read(){
    int x = 0, flag = 1;char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') flag = - 1;
        ch = getchar();
    }
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return x * flag;
}

const int N = 3e5 + 5;
int a[N], k;
LL f[N], ans = -1e9;
vector <int> G[N];

inline void dfs(int x ,int fa, bool c) {
    f[x] = a[x];
    for(int i = 0; i < G[x].size(); ++i) {
        int to = G[x][i];
        if(to == fa) continue;
        dfs(to, x, c);
        f[x] += max(f[to], 0LL);
    }
    if(c) ans = max(ans, f[x]);
    else if(f[x] == ans) {
        f[x] = 0;
        k ++;
    }
}

int main() {   
    int n = read();
    REP(i, 1, n) a[i] = read();
    REP(i, 1, n - 1) {
        int a = read(), b = read();
        G[a].push_back(b); G[b].push_back(a);
    }
    dfs(1, 0, 1);
    dfs(1, 0, 0);
    printf("%lld %d\n", ans * k, k);
	return 0;
}

1088F - Ehab and a weird weight formula

You’re given a tree consisting of ? nodes. Every node ? has a weight ??. It is guaranteed that there is only one node with minimum weight in the tree. For every node ? (except for the node with the minimum weight), it must have a neighbor ? such that ??<??. You should construct a tree to minimize the weight ? calculated as follows:

For every node ?, ????⋅?? is added to ? (???? is the number of edges containing node ?).
For every edge {?,?}, ⌈???2(????(?,?))⌉⋅???(??,??) is added to ?, where ????(?,?) is the number of edges in the path from ? to ? in the given tree.
Input
The first line contains the integer ? (2≤?≤5⋅105), the number of nodes in the tree.

The second line contains ? space-separated integers ?1,?2,…,?? (1≤??≤109), the weights of the nodes.

The next ?−1 lines, each contains 2 space-separated integers ? and ? (1≤?,?≤?) which means there’s an edge between ? and ?.

Output
Output one integer, the minimum possible value for ?.

First, let’s reduce the problem to ordinary MST. We know that each edge {u, v} adds ⌈log2(dist(u, v))⌉·min(au, av) to w. In fact, it also adds 1 to degu and degv. Thus, the problem is ordinary MST on a complete graph where each edge {u, v} has weight (⌈log2(dist(u, v))⌉ + 1)·min(au, av) + max(au, av)!

Let the node with the minimum weight be m. Let’s root the tree at it.

Lemma: for every node u and a child v, av > au. In simpler words, the weight increase as we go down the tree.

Proof: the proof is by contradiction. Assume av ≤ au. Then, the condition in the problem (that every node has an adjacent node with less weight) isn’t satisfied yet for v. Therefore, v must have a child k such that ak < av. However, the condition isn’t satisfied for k, so k needs another child and the child needs another child etc. (the tree will be infinite) which is clearly a contradiction.

From that, we know that the weights decrease as we go up the tree and increase as we go down.

Back to the MST problem. From Kruskal’s algorithm, we know that the minimal edge incident to every node will be added to the MST (because the edges are sorted by weight). Let’s analyze the minimal edge incident to every node u. Let its other end be v. Except for node m, v will be an ancestor of u. Why? Assume we fix the distance part and just want to minimize av. We’ll keep going up the tree (it’s never optimal to go down, since the weights will increase) until we reach the desired distance. Now, since the minimal edge incident to every node will be added to the MST (by Kruskal’s algorithm), and they’re distinct (because, otherwise, you’re saying that u is an ancestor of v and v is an ancestor of u), THEY ARE THE MST. Now, the problem just reduces to finding the minimal edge incident to every node and summing them up (except for m). To do that, we’ll fix the ⌈log2(dist(u, v))⌉ (let it be k), and get the 2kth ancestor with the well-known sparse-table (binary lifting).

code:

#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
vector<int> v[500005];
int m=1,a[500005],dp[20][500005];
long long ans;
void dfs(int node,int p)
{
    dp[0][node]=p;
    for (int i=1;i<20;i++)
    {
        if (dp[i-1][node]!=-1)
        dp[i][node]=dp[i-1][dp[i-1][node]];
    }
    int d;
    long long mn=(1LL<<60);
    for (d=0;d<20 && dp[d][node]!=-1;d++)
    mn=min(mn,(long long)(d+1)*a[dp[d][node]]+a[node]);
    mn=min(mn,(long long)(d+1)*a[m]+a[node]);
    if (p!=-1)
    ans+=mn;
    for (int u:v[node])
    {
        if (u!=p)
        dfs(u,node);
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if (a[i]<a[m])
        m=i;
    }
    for (int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        v[a].push_back(b);
        v[b].push_back(a);
    }
    memset(dp,-1,sizeof(dp));
    dfs(m,-1);
    printf("%I64d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值