VP记录:Codeforces Round 599 (Div. 2) A~D

传送门:CF

前提提要:无

A题:A. Maximum Square

刚开始的第一个想法是排序然后二分答案.但是一看范围才1000,果断直接使用暴力枚举.
考虑枚举最终的答案,然后记录有多少个 a i ai ai大于此值,然后判断能否构成一个正方形即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn];
int main() {
	int T=read();
	while(T--) {
		int n=read();
		for(int i=1;i<=n;i++) {
			a[i]=read();
		}
		for(int i=n;i>=1;i--) {
			int cnt=0;
			for(int j=1;j<=n;j++) {
				if(a[j]>=i) cnt++;
			}
			if(cnt>=i) {
				cout<<i<<endl;
				break;
			}
		}
	}
	return 0;
}

B1题:B1. Character Swap (Easy Version)

发现仅且必须调换一次.所以当我们的两个字符串有大于2的位置的字符不一样时,我们无论如何都是无法用一次花费使他们相同的.所以此时的答案就是"NO".当然当我们只有一个位置的字符不一样时同样是"NO".当然当我们没有位置不一样的时候肯定是"YES".

然后我们考虑恰好有两个位置不一样的情况.我们此时进行的操作显然是将 a [ p o s 1 ] a[pos1] a[pos1] b [ p o s 2 ] b[pos2] b[pos2]进行调换(或者 a [ p o s 2 ] a[pos2] a[pos2] b [ p o s 1 ] b[pos1] b[pos1]进行调换).所以此时我们就需要 a [ p o s 1 ] a[pos1] a[pos1] a [ p o s 2 ] a[pos2] a[pos2]相同,并且 b [ p o s 1 ] b[pos1] b[pos1] b [ p o s 2 ] b[pos2] b[pos2]相同.

给出的代码有部分实现不是很严谨,利用了某些特性,但是我懒得修改了(逃

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int main() {
	int T=read();
	while(T--) {
		int n=read();
		string s,t;
		cin>>s>>t;int cnt=0;
		for(int i=0;i<s.length();i++) {
			cnt+=s[i]!=t[i]?1:0;
		}
		if(cnt>2) {
			cout<<"No"<<endl;
			continue;
		}
		vector<char>a,b;
		for(int i=0;i<s.length();i++) {
			if(s[i]!=t[i]) {
				a.push_back(s[i]);
				b.push_back(t[i]);
			}
		}
		if(a[0]==a[1]&&b[1]==b[0]) {
			cout<<"Yes"<<endl;
		}
		else {
			cout<<"No"<<endl;
		}
	}
	return 0;
}

B2题:B2. Character Swap (Hard Version)

简单思维题.本题的突破点应该是2*n.往这个靠思路很快就可以出来了.
考虑逐位枚举 s s s字符串的每一位,然后使用操作使 t t t字符串的每一位和 s s s一样.
其实我们可以发现一个小性质.就是我们如果想将 t [ p o s ] t[pos] t[pos]的字符换到 t [ i ] t[i] t[i]位置时,我们只需要两步就可以了.先进行<pos,pos>,然后<pos,i>.然后假如我们想将 s [ p o s ] s[pos] s[pos]换到 t [ i ] t[i] t[i]的位置,只需要进行一步.<pos,i>.
使用这个性质这道题就不难解决了.因为两个字符串最终是相同的.也就是每一个字符都平均的分配到两个字符串中.此时我们可以先来解决"NO"的情况.显然当有一个字符的数量是奇数的时候我们就不能进行分配了,此时的情况就是"NO".其他的情况都是"YES".
因为当我们使用上述的性质时,对于每一位的最大花费也就是2,最终必然是小于2*n,也就是说只要我们能使他们相同,我们就能在2*n的步骤内使他们相同
然后考虑当 s [ i ] ! = t [ i ] s[i]!=t[i] s[i]!=t[i]的情况,我们先来计算一下s里面有多少个 s [ i ] s[i] s[i],假设我们此时的个数大于总数的一半时,此时我们应该将s里面的字符串换到t里面.使用之前所说的第二个操作即可.
当我们此时的个数小于总数的一半时,此时我们应该将t里面的字符串换到s里面,使用第一个操作即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int main() {
	int T=read();
	while(T--) {
		int n=read();
		string s,t;
		cin>>s>>t;int ans=0;
		map<int,int>mp;
		for(int i=0;i<s.length();i++) {
			mp[s[i]-'a']++;
			mp[t[i]-'a']++;
		}
		int flag=0;
		for(auto it : mp) {
			if(it.second&1) {
				flag=1;break;
			}
		}
		if(flag) {
			cout<<"No"<<endl;
			continue;
		}
		vector<pair<int,int> >v;
		for(int i=0;i<s.length();i++) {
			if(s[i]!=t[i]) {
				map<int,int>mp1;int pos;
				for(int j=i;j<s.length();j++) {
					mp1[s[j]-'a']++;
					if(s[j]==s[i]&&j>i) {
						pos=j;
					}
				}
				if(mp1[s[i]-'a']*2>mp[s[i]-'a']) {
					v.push_back({pos,i});
					ans++;
					swap(s[pos],t[i]);
				}
				else {
					int pos;
					for(int j=i+1;j<t.length();j++) {
						if(t[j]==s[i]) {
							pos=j;
							break;
						}
					}
					v.push_back({pos,pos});
					swap(s[pos],t[pos]);ans++;
					v.push_back({pos,i});
					swap(s[pos],t[i]);ans++;
				}
			}
			mp[s[i]-'a']-=2;
		}
		cout<<"Yes"<<endl;
		cout<<ans<<endl;
		for(int i=0;i<v.size();i++) {
			cout<<v[i].first+1<<" "<<v[i].second+1<<endl;
		}
	}
	return 0;
}

C题:C. Tile Painting

简单数论题.读完题意之后不难发现应该往质因数那边靠.
因为当有一个 k ∣ a k|a ka时,此时我们可以使用步长为 k k k来实现步长为 a a a的情况,所以只要考虑质因数的情况即可.
那么首先当我们此时的数只有一个质因数的时候,随便推一下就是发现答案应该是这个质因数-1
然后考虑不止一个质因数的时候,其实简单手模一下就会发现此时我们的答案无论如何都是1.诶这是怎么回事呢.下面简单来证明一下这个结论.
假设此时我们有两个质因数,不妨记为 a , b a,b a,b.然后我们此时有 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1.然后根据裴蜀定理,我们此时必然存在整数 x , y x,y x,y满足 a x + b y = 1 ax+by=1 ax+by=1,然后我们就必然会有 k x , k y kx,ky kx,ky满足 a ∗ k x + b ∗ k y = k a*kx+b*ky=k akx+bky=k,也就是说,我们可以从1这个点达到任何一个其他点!!.
证毕
需要注意的是1要特判且需要开longlong

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
#define int long long
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
signed main() {
	int n=read();int temp=n;map<int,int>mp;
	if(n==1) {
		cout<<1<<endl;
		return 0;
	}
	for(int j=2;j<=__builtin_sqrt(n);j++) {
		while(temp%j==0) {
			mp[j]++;
			temp/=j;
		}
	}		
	if(temp!=1) mp[temp]++;
	if(mp.size()==1) cout<<(*mp.begin()).first<<endl;
	else cout<<1<<endl;
	return 0;
}

D题:D. 0-1 MST

一道比较难的图论题?
首先我们应该有一个比较自然的想法就是:
1.先首先选一个点,作为初始点u
2.然后在所有的没有被加入到联通块中的v中找出长度为0的<u,v>
3.贪心的去想,这个0边显然是需要被加入的,直接加入0边
4.目前已经加入了所有到当前联通块的0边,此时没有0边可以加了,必须要加1边,那就随便取一个1边连到当前的联通块(注:此时的联通块指正在扩展的最小生成树).因为当前联通块新增了一个点,所以又可能有0边存在,所以继续找0边.

显然这个想法的关键点是如何快速的找到0边所对应的v.直接枚举显然是不可行的.这样复杂度将会爆炸.此时就有一个比较巧妙的解决方案,考虑对u的所有1边所对应的v的权值+1.然后使用线段树来维护所有点的权值最小值以及最小值所在的位置.然后我们每一次都取出权值最小的vm.显然这个vm是最有可能与联通块有0边的v.假设此时我们的联通块中的点的个数是 i i i.那么此时的vm的权值只要比i要小,此时必然就与联通块有0边存在(因为假设没有0边存在,意味着之前的所有点都与v有连边,也就是每一个点都会给v加一权值),反之则无.如果只有1边的话就将花费+1.然后再将vm作为u继续扩展.还需要注意的一个小细节就是已经加入联通块中的点显然不需要继续扩展了,所以我们可以将其的权值赋为inf

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int n,m;
vector<int>edge[maxn];
struct Segment_tree{
	int l,r,mn,mnpos;
}tree[maxn];
void pushup(int rt) {
	tree[rt].mn=min(tree[ls].mn,tree[rs].mn);
	if(tree[rt].mn==tree[ls].mn) tree[rt].mnpos=tree[ls].mnpos;
	else tree[rt].mnpos=tree[rs].mnpos;
}
void build(int l,int r,int rt) {
	tree[rt].l=l;tree[rt].r=r;
	if(l==r) {
		tree[rt].mn=0;
		tree[rt].mnpos=l;
		return ;
	}
	int mid=(l+r)>>1;
	build(lson);build(rson);
	pushup(rt);
}
void update(int pos,int rt,int val) {
	if(tree[rt].l==pos&&tree[rt].r==pos) {
		tree[rt].mn+=val;
		return ;
	}
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if(pos<=mid) update(pos,ls,val);
	else update(pos,rs,val);
	pushup(rt);
}
int main() {
	n=read();m=read();
	for(int i=1;i<=m;i++) {
		int u=read(),v=read();
		edge[u].push_back(v);
		edge[v].push_back(u);
	}
	build(root);int ans=0;
	update(1,1,1e9);
	for(int j=0;j<edge[1].size();j++) {
		int v=edge[1][j];
		update(v,1,1);
	}
	for(int i=2;i<=n;i++) {
		int u=tree[1].mnpos;
		if(tree[1].mn>=i-1) ans++;
		update(u,1,1e9);
		for(int j=0;j<edge[u].size();j++) {
			int v=edge[u][j];
			update(v,1,1);
		}
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值