ICPC2024 上海站

B. Basic Graph Algorithm

如果说一个点的相邻节点都走完了,就返回

如果没走完,并且排列中下一个位置和它相邻,就走;如果不相邻,就加边

需要注意的是,对于每一个点的相邻点用 \(vector\) 存储,并且需要按出点在给出排列中的顺序排序,以保证时间复杂度

code
#include<bits/stdc++.h>
using namespace std;
const int NN = 3e5 + 8, MM = 5e5 + 8;
int n,m;
int p[NN],revp[NN];
bool vis[NN];
struct Edge{
	int u,v;
};
vector<int> E[NN];
vector<Edge> ans,e;
void dfs(int x,int &pos){
	pos++;
	if(vis[x]) return;
	vis[x] = 1;
	for(int i = E[x].size()-1; i >= 0; --i){
		int v = E[x][i];
		if(vis[v]) continue;
		while(pos < v){
			ans.push_back({p[x],p[pos]});
			dfs(pos,pos);
		}
		if(vis[v]) continue;
		if(pos == v){
			dfs(pos,pos);
		}
	}
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for(int i = 1,u,v; i <= m; ++i){
		cin >> u >> v;
		e.push_back({u,v});
	}
	for(int i = 1; i <= n; ++i) cin >> p[i], revp[p[i]] = i, E[0].push_back(i);
	
	for(int i = 0; i < m; ++i){
		int u = e[i].u, v = e[i].v;
		E[revp[u]].push_back(revp[v]),E[revp[v]].push_back(revp[u]);
	}
	
	for(int i = 0; i <= n; ++i) sort(E[i].begin(),E[i].end(),greater<int>());
	int pos = 0;
	dfs(0,pos);
	cout << ans.size() << '\n';
	for(int i = 0; i < ans.size(); ++i) cout << ans[i].u << ' ' << ans[i].v << '\n';
} 

C. Conquer the Multiples

可以发现,两个人分别在奇数和偶数上行走,同时奇数可以 \(ban\) 掉偶数位置,但偶数不能 \(ban\) 掉奇数位置,所以说小小分讨即可。

code
#include<bits/stdc++.h>
using namespace std;
int T;
int l,r;
string P[2] = {"Alice", "Bob"};
void solve(){
	cin >> l >> r;
	if(l&1){
		if(l*2 <= r) cout << P[0] << '\n';
		else cout << P[(r-l)&1] << '\n';
	}
	else{
		if(l == r) cout << P[0] << '\n';
		else{
			++l; swap(P[0],P[1]);
			if(l*2 <= r) cout << P[0] << '\n';
			else cout << P[(r-l)&1] << '\n';
			swap(P[0],P[1]);
		}
	}
}
int main(){
	cin >> T;
	while(T--){
		solve();
	}
}

D. Decrease and Swap

tag: 性质

很好的性质题,使我旋转

本人在 VP 时天真的认为我们的 \(10^{18}\)\(1\) 完全等价,但显然并不是

我们言归正传,首先肯定是需要分类讨论的:

末尾 \(00\):直接满足条件

末尾 \(01\):需要 \(01101\) / \(10101\) / \(11001\) (即倒数 \(4,5,6\) 位至少 \(2\)\(1\))

末尾 \(10\): 需要 \(\_1\_10\) / \(1\_110\)

末尾 \(11\):同 \(10\)


这是只考虑后 \(5\) 位的情况,但是我们的前面的 \(1\) 也有可能被挪到后面来

只要一段区间内 \(cnt0 < cnt1\) 那么区间的末尾就一定能通过操作变成 \(1\)

于是我们就得到的判定一个位置是否能变成 \(1\) 的方法

最后这道题就解决啦!

code
#include<bits/stdc++.h>
using namespace std;
const int NN = 1e6 + 8;
int T,n;
string s;
int check(int i){
	int cnt0 = 0, cnt1 = 0;
	for(int j = i; j >= 1; --j){
		if(s[j] == '0') ++cnt0;
		else ++cnt1;
		if(cnt1 > cnt0) return 1;
	}
	return 0;
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> T;
	while(T--){
		cin >> n >> s;
		s = ' ' + s;
		if(s[n-1] == '0')
			if(s[n] == '0') puts("Yes");//00
			else puts(check(n-2) && (check(n-3) || check(n-4)) ? "Yes" : "No");//01
		else puts(check(n-3) || (check(n-4) && s[n-2] == '1') ? "Yes" : "No");//1?
	}
	return 0;
}

G. Geometry Task

tag: 二分答案 贪心

我们通过二分中位数,我们可以把验证答案的问题变为:

给每一个区间分配一个下标,若满足该下标在这个区间之内,则贡献为 \(1\),求最大贡献。

这可以通过扫到区间左端点,在优先队列中加入右端点,每次选择当前优先队列中右端点最小的区间进行满足的方式得到最优解

(当然这道题因为所有区间要么覆盖最左端,要么覆盖最右端,所以可以两边分别贪心做)

code
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const int NN = 1e5 + 8;
int T;
int n;
ll a[NN],b[NN],c[NN];

vector<int> p[NN];
priority_queue<int, vector<int>, greater<int> > q;

int erff(int i, ll num)// a[i] < 0
{
    int l = 1, r = n,ans = -1;
    while(l <= r)
    {
        int mid = (l + r) / 2;
        if(a[i] * c[mid] + b[i] >= num) l = mid + 1, ans = mid;
        else r = mid - 1;
    }
    return ans;
}
int erfz(int i, ll num)// a[i] > 0
{
    int l = 1, r = n,ans = -1;
    while(l <= r)
    {
        int mid = (l + r) / 2;
        if(a[i] * c[mid] + b[i] >= num) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    return ans;
}

bool check(ll num)
{
    // cerr << "check:" << num << endl;
    for(int i = 1; i <= n; ++i) p[i].clear();
    for(int i = 1; i <= n; ++i)
    {
        if(a[i] == 0)
        {
            if(b[i] >= num) p[1].push_back(n);
        }
        else if(a[i] < 0)
        {
            int rb = erff(i,num);
            if(rb != -1) p[1].push_back(rb);
        }
        else if(a[i] > 0)
        {
            int lb = erfz(i,num);
            if(lb != -1) p[lb].push_back(n);
        }
    }//get the acceptable internal of each line

    while(!q.empty()) q.pop();
    int cnt = 0;
    for(int i = 1; i <= n; ++i)
    {
        for(int j : p[i]) q.push(j);
        while(!q.empty() && q.top() < i) q.pop();
        if(!q.empty())
        {
            q.pop(); ++cnt;
        }
    }
    // cerr << "cnt:1" << cnt << endl;
    return cnt >= (n + 1) / 2;
}
void solve()
{
    cin >> n;
    for(int i = 1; i <= n; ++i) cin >> a[i];
    for(int i = 1; i <= n; ++i) cin >> b[i];
    for(int i = 1; i <= n; ++i) cin >> c[i];
    sort(c+1,c+1+n);

    ll l = -2e18, r = 2e18,ans = l;
    while(l <= r)
    {
        ll mid = (l + r) / 2;
        if(check(mid)) l = mid + 1,ans = mid;
        else r = mid - 1;
    }
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(false),cin.tie(0);
    cin >> T;
    while(T--)
    {
        solve();
    }
    return 0;
}

I. In Search of the Ultimate Artifact

签到题,去除所有 \(0\) 后,从大到小合并即可

注意:\(0\leq a_i \leq 10^9, mod = 998244353\),所以若输出 \(a_i\),一定要取模!

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN = 2e5 + 8, MOD = 998244353;
int T;
int n,k;
ll p[NN];
void solve()
{
    bool vis = 0;
    cin >> n >> k;
    for(int i = 1; i <= n; ++i) cin >> p[i];
    sort(p+1,p+1+n);
    ll ans = 1;
    // cout << 1 << endl;
    for(int i = n; i >= 1; --i)
    {
        int pos = i-k+1+vis;
        if(pos > 0 && p[pos] != 0)
        {
            for(int j = pos; j <= i; ++j) ans = ans * p[j] % MOD;//cout << p[j] << endl;;
            i = pos;
            vis = 1;
            // cout << '-' << endl;
        }
    }
    if(vis) cout << ans << endl;
    else cout << p[n] % MOD << endl;
}
int main()
{
    // ios::sync_with_stdio(false),cin.tie(0);
    cin >> T;
    while(T--)
    {
        solve();
    }
}
posted @ 2025-10-14 17:29  ricky_lin  阅读(170)  评论(0)    收藏  举报