专栏文章

Atcoder ABC 393

题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miq8j662
此快照首次捕获于
2025/12/04 00:42
3 个月前
此快照最后确认于
2025/12/04 00:42
3 个月前
查看原文
给出NN个点和MM条边的无向图,最少需要删除多少边使得这个图成为简单图, 即没有重边和自环。
  • 遇到自环直接删除。
  • mapmap记录重边
CPP
#include<bits/stdc++.h>
using namespace std;
map<pair<int, int>, int>mp;
// mp [{u,v}] -> u到v的边有没有出现过
int main(){
	int n, m;
	cin >> n >> m;
	int ans = 0;
	
	for(int i = 1; i <= m; i++){
		int u, v;
		cin >> u >> v;
		//默认让小的点 连向大的点
		if(u > v) swap(u, v);
		if(u == v) ans++;
		else if(mp.count({u, v})) ans++;
		mp[{u, v}] = 1;
	}	
	cout << ans << endl;
	return 0;
}
先确定移动的位置,可以大胆猜一下,往哪个1去靠。 显然往中间的1去靠,比边上的1会更优。类似中位数求最短距离一样。
把所有是1的位置都找出来,放入pospos数组中,可以先求出中位数的位置midmid
对于第ii1,它贡献的答案为abs(pos[mid]pos[i](midi))abs(pos[mid] - pos[i] - (mid - i))
CPP
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){

    int n;
    string s;
    cin >> n >> s;

    vector<int> pos;
    
    for(int i = 0; i < n; i++)
        if (s[i] == '1')
            pos.push_back(i);
            
    int cnt = pos.size();

    if (cnt <= 1){
        cout << 0 << endl;
        return 0;
    }
	 
    int mid = cnt / 2, ans = 0;
    for (int i = 0; i < pos.size(); i++)
        ans += abs(pos[mid] - (mid - i) - pos[i]);

    cout << ans << endl;

    return 0;
}
解法2:
考虑对0的移动,对于第ii位上的0,统计左侧11的个数为xx,右侧11的个数为yy
最终的得到的字符串形状:0...01...10..0,前面0的数量可能是00个,后面0的数量可能是00个。
从左往右考虑`0',它对答案的贡献是min(x,y);min(x, y);
思路:考虑枚举最大公约数dd ,再看 dd倍数个数kk 的关系,来决定 dd 能不能作为答案。
做法:
  • mp[i]mp[i]统计数字ii的数量 ,ans[i]ans[i]表示数字ii的答案。
  • 枚举当前最大公约数dd, 再枚举dd的倍数,得到原数组中有约数的数量为cntcnt个。
  • cnt>=kcnt >= k, 则说明一定能从原数组中选出k个数,使得他们的最大公约数至少为dd,所以可以把所有dd的倍数的答案更新为dd
  • 考虑完所有的dd后,此时ans[i]ans[i]就是数字i的最优答案。
例如样例1:
3 4 6 7 12
dd == 11时, 则当前算出ans[3]=ans[4]=ans[6]=ans[7]=ans[12]=1ans[3] = ans[4] = ans[6] = ans[7] = ans[12] = 1
dd == 22时,更新ans[4]=ans[6]=ans[12]=2ans[4] = ans[6] = ans[12] = 2
dd == 33时,更新ans[3]=ans[6]=ans[12]=3ans[3] = ans[6] = ans[12] = 3
...
CPP
#include<bits/stdc++.h>
using namespace std;
const int N = 12e5 + 10;

int a[N], mp[N], ans[N];

signed main(){
	int n, k;
	cin >> n >> k;
	int lim = 0;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		mp[a[i]]++;
		lim = max(lim, a[i]);
	}
	
	for(int d = 1; d <= lim; d++){
		int cnt = 0;
		for(int i = d; i <= lim; i += d)
			cnt += mp[i];
		
		if(cnt >= k){
			for(int i = d; i <= lim; i += d)
				ans[i] = d;
		}
	}	
	for(int i = 1; i <= n; i++)
		cout << ans[a[i]] << endl;                             
	return 0;
}
根据DPDPLISLIS的思想,设 f[i]f[i] 为当前项时长度为 iiLISLIS序列末项最小值,
由于 f[i]f[i] 具有递增性,每次二分查找在数组 ff 中第一个大于等于 a[i]a[i] 的位置更新。
离线查询,每次在对应的r的位置,二分求在当前状态下数组ff中最后一个不大于xx的位置需要即为答案。
CPP
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10, inf = 1e18;
int n, q, a[N], f[N];

struct node{
	int id, r, x, ans;
}qry[N];

bool cmp(node A, node B){
	return A.r < B.r;
}

bool cmp2(node A, node B){
	return A.id < B.id;
}

signed main(){
	scanf("%lld%lld", &n, &q);
	for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
	for(int i = 1; i <= q; i++){
		int r, x;
		scanf("%lld%lld", &r, &x);
		qry[i] = {i, r, x, 0};
	}
	
	sort(qry + 1, qry + q + 1, cmp);
	
	for(int i = 1; i <= n; i++) f[i] = inf;
	
	f[0] = 0;
	for(int i = 1, j = 1; i <= n; i++){
		int k = lower_bound(f, f + n + 1, a[i]) - f;
		f[k] = min(f[k], a[i]);
		
		while(j <= q && qry[j].r == i){
			qry[j].ans = upper_bound(f, f + n + 1, qry[j].x) - f - 1;
			j++;
		}
	}
	
	sort(qry + 1, qry + q + 1, cmp2);
	
	for(int i = 1; i <= q; i++)
		printf("%lld\n", qry[i].ans);
	
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...