VP Educational Codeforces Round 22


A. The Contest

题意:有n个数的和sum,在m的个区间里如果lisumri则输出sum,否则如果lisum,输出li

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    int m;
    std::cin >> m;
    std::vector<int> l(m), r(m);
    for (int i = 0; i < m; ++ i) {
    	std::cin >> l[i] >> r[i];
    }

    i64 sum = std::accumulate(a.begin(), a.end(), 0ll);
    for (int i = 0; i < m; ++ i) {
    	if (l[i] >= sum) {
    		std::cout << l[i] << "\n";
    		return;
    	} else if (l[i] <= sum && r[i] >= sum) {
    		std::cout << sum << "\n";
    		return;
    	}
    }

    std::cout << -1 << "\n";
}

B. The Golden Age

题意:给你一个x,y,求所有xa+yb[l,r]里构成的最大间隙。

因为是幂,则数量很少,可以直接求出所有在[l,r]数。然后排序后枚举两个相邻的数取最大差就行。

点击查看代码
void solve() {
    i64 x, y, l, r;
    std::cin >> x >> y >> l >> r;
    std::vector<i64> a, b;
    for (i128 i = 1; i <= r; i *= x) {
		a.push_back(i);
    }

    for (i128 i = 1; i <= r; i *= y) {
		b.push_back(i);
    }

    std::vector<i64> c;
    for (auto & i : a) {
    	for (auto & j : b) {
    		if (i + j >= l && i + j <= r) {
	    		c.push_back(i + j);
    		}
    	}
    }

    c.push_back(l - 1);
    c.push_back(r + 1);
    std::sort(c.begin(), c.end());
    int n = c.size();
    i64 ans = 0;
    for (int i = 1; i < n; ++ i) {
    	// std::cout << c[i] << " \n"[i == n - 1];
    	ans = std::max(ans, c[i] - c[i - 1] - 1);
    }

    std::cout << ans << "\n";
}

C. The Tag Game

题意:在一棵树上,Alcie1点,Bobx点,两个人轮流移动,Bob先动,可以停留在当前点上,求Alice几回合后可以捉到Bob

以这两个点为根做两次bfs,求出每个点到这两个点的距离。如果这个这个点Bob可以比Alice先到达,那么就可以取Alice到这个点的最大值,因为Bob到这个点后假设一直不动,就需要Alice跑过来。而最优情况肯定是Bob一直跑,直到一个不能跑了就一直停留。

点击查看代码
void solve() {
    int n, x;
    std::cin >> n >> x;
    -- x;
    std::vector<std::vector<int>> adj(n);
    for (int i = 1; i < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }

    std::vector d(2, std::vector<int>(n, -1));
    auto work = [&](int s, int i) -> void {
    	std::queue<int> q;
    	q.push(s);
    	d[i][s] = 0;
    	while (q.size()) {
    		int u = q.front(); q.pop();
    		for (auto & v : adj[u]) {
    			if (d[i][v] == -1) {
    				d[i][v] = d[i][u] + 1;
    				q.push(v);
    			}
    		}
    	}
    };

    work(0, 0); work(x, 1);
    int ans = 0;
    for (int i = 0; i < n; ++ i) {
    	if (d[1][i] < d[0][i]) {
    		ans = std::max(ans, d[0][i]);
    	}
    }

    std::cout << ans * 2 << "\n";
}

D. Two Melodies

题意:在一个数组种选两个不相交的子序列,每个子序列相邻的两个数要么相差1,要么余7相同。使得两个子序列长度加起来最大。

如果我们向每个|aiaj|=1aiaj(mod7)(i,j)ij连边。那么就等价于求图里的两段路径,使得路径长度之和最大,这是个经典费用流问题。我们把每个点拆成出度和入度,一个点的入度向出度连费用为1流量为1的边,可以连边的由出度向入度连费用为0流量为1的边,同时源点给每个点的入度连费用为0流量为1的边,每个点的出度向汇点连费用为0流量为1的边,然后为了满足两条路径的需求,超级源点向源点连费用为0流量为2的边,汇点向超级汇点连费用为0流量为2的边。但这样边数太大,时间复杂度不能接受。
我们可以对于每个i的出度,往右边最近的aiaj=1以及aiaj=1j的入度连边,因为满足条件的aj值只有两种,我们选最近的可以给后面留出更大空间,显然更优。然后对于aiaj(mod7)的最近的j,我们由i的出度向j的入度连边,同时i的入度向j的入度连边,这意味着选了i可以继续选j,或者如果不选i下一个就可以选j。这样就解决了。
这个问题可以由2条路径扩展成k条路径,只需要该超级源点和超级汇点与源点汇点之间的流量。

点击查看代码
template<class T>
struct MinCostFlow {
    struct _Edge {
        int to;
        T cap;
        T cost;
        _Edge(int to_, T cap_, T cost_) : to(to_), cap(cap_), cost(cost_) {}
    };
    int n;
    std::vector<_Edge> e;
    std::vector<std::vector<int>> g;
    std::vector<T> h, dis;
    std::vector<int> pre;
    bool dijkstra(int s, int t) {
        dis.assign(n, std::numeric_limits<T>::max());
        pre.assign(n, -1);
        std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>, std::greater<std::pair<T, int>>> que;
        dis[s] = 0;
        que.emplace(0, s);
        while (!que.empty()) {
            T d = que.top().first;
            int u = que.top().second;
            que.pop();
            if (dis[u] != d) {
                continue;
            }
            for (int i : g[u]) {
                int v = e[i].to;
                T cap = e[i].cap;
                T cost = e[i].cost;
                if (cap > 0 && dis[v] > d + h[u] - h[v] + cost) {
                    dis[v] = d + h[u] - h[v] + cost;
                    pre[v] = i;
                    que.emplace(dis[v], v);
                }
            }
        }
        return dis[t] != std::numeric_limits<T>::max();
    }
    MinCostFlow() {}
    MinCostFlow(int n_) {
        init(n_);
    }
    void init(int n_) {
        n = n_;
        e.clear();
        g.assign(n, {});
    }
    void addEdge(int u, int v, T cap, T cost) {
        g[u].push_back(e.size());
        e.emplace_back(v, cap, cost);
        g[v].push_back(e.size());
        e.emplace_back(u, 0, -cost);
    }
    std::pair<T, T> flow(int s, int t) {
        T flow = 0;
        T cost = 0;
        h.assign(n, 0);
        while (dijkstra(s, t)) {
            for (int i = 0; i < n; ++i) {
                h[i] += dis[i];
            }
            T aug = std::numeric_limits<int>::max();
            for (int i = t; i != s; i = e[pre[i] ^ 1].to) {
                aug = std::min(aug, e[pre[i]].cap);
            }
            for (int i = t; i != s; i = e[pre[i] ^ 1].to) {
                e[pre[i]].cap -= aug;
                e[pre[i] ^ 1].cap += aug;
            }
            flow += aug;
            cost += aug * h[t];
        }
        return std::make_pair(flow, cost);
    }
    struct Edge {
        int from;
        int to;
        T cap;
        T cost;
        T flow;
    };
    std::vector<Edge> edges() {
        std::vector<Edge> a;
        for (int i = 0; i < e.size(); i += 2) {
            Edge x;
            x.from = e[i + 1].to;
            x.to = e[i].to;
            x.cap = e[i].cap + e[i + 1].cap;
            x.cost = e[i].cost;
            x.flow = e[i + 1].cap;
            a.push_back(x);
        }
        return a;
    }
};


void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    MinCostFlow<int> mf(2 * n + 4);
    int s = 2 * n, t = 2 * n + 1, S = 2 * n + 2, T = 2 * n + 3;
    mf.addEdge(S, s, 2, 0);
    mf.addEdge(t, T, 2, 0);

    for (int i = 0; i < n; ++ i) {
    	mf.addEdge(s, i, 1, 0);
    	mf.addEdge(i + n, t, 1, 0);
    	mf.addEdge(i, i + n, 1, -1);
    	for (int j = i + 1; j < n; ++ j) {
    		if (a[i] == a[j] + 1) {
    			mf.addEdge(i + n, j, 1, 0);
    			break;
    		}
    	}

    	for (int j = i + 1; j < n; ++ j) {
    		if (a[i] == a[j] - 1) {
    			mf.addEdge(i + n, j, 1, 0);
    			break;
    		}
    	}

    	for (int j = i + 1; j < n; ++ j) {
    		if (a[i] % 7 == a[j] % 7) {
    			mf.addEdge(i + n, j, 1, 0);
    			mf.addEdge(i, j, 1, 0);
    			break;
    		}
    	}
    }

    std::cout << -mf.flow(S, T).second << "\n";
}

E. Army Creation

题意:有n个数,每个类型的数最多拿k个,每次求[l,r]这个区间里可以选几个数。

i的左边的第k个相同数的位置pi,没有则pi=0。那么对于[l,r]的数,我们先全部选上,然后看要删掉哪些数,对于pili的数就不能选。则我们用主席树维护这个pi的区间和,看[l,r]这个区间有多少数的pi[l,r]之间。

点击查看代码
#define ls(u) tr[u].lson
#define rs(u) tr[u].rson

const int N = 1e5 + 5;

struct Node {
	int lson, rson;
	int sum;
}tr[N << 5];
int tot = 0;

int root[N];

void insert(int & u, int v, int l, int r, int p) {
	u = ++ tot;
	tr[u] = tr[v];

	tr[u].sum += 1;
	if (l == r) {
		return;
	}

	int mid = l + r >> 1;
	if (p <= mid) {
		insert(ls(u), ls(v), l, mid, p);
	} else {
		insert(rs(u), rs(v), mid + 1, r, p);
	}
}

int query(int u, int v, int l, int r, int L, int R) {
	if (!u) {
		return 0;
	}

	if (L <= l && r <= R) {
		return tr[u].sum - tr[v].sum;
	}

	int mid = l + r >> 1;
	if (R <= mid) {
		return query(ls(u), ls(v), l, mid, L, R);
	} else if (L > mid) {
		return query(rs(u), rs(v), mid + 1, r, L, R);
	}

	return query(ls(u), ls(v), l, mid, L, mid) + query(rs(u), rs(v), mid + 1, r, mid + 1, R);
}

void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<std::vector<int>> pos(N);
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	std::cin >> a[i];
    	pos[a[i]].push_back(i);
    	int p = 0;
    	if (pos[a[i]].size() > k) {
    		p = pos[a[i]][(int)pos[a[i]].size() - 1 - k];
    	}

    	insert(root[i], root[i - 1], 0, n, p);
    }

    int q;
    std::cin >> q;
    int last = 0;
    while (q -- ) {
    	int l, r;
    	std::cin >> l >> r;
    	l = (l + last) % n + 1;
    	r = (r + last) % n + 1;
    	if (l > r) {
    		std::swap(l, r);
    	}

    	last = r - l + 1 - query(root[r], root[l - 1], 0, n, l, r);
    	std::cout << last << "\n";
    }
}
posted @   maburb  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示