Codeforces Round #598 (Div. 3) 题解

63 阅读5分钟

比赛链接

A

思路

题目要构造一个式子:nx+y=S(xa,yb)nx+y=S(x\leq a,y\leq b)。考虑找一个最大的x1x_1使得nx1Snx_1\leq S,剩下的部分用yy补,如果x1>ax_1>a,就把x1x_1换为aa继续用yy补剩下的。判断是否能凑出来。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
typedef long long ll;
using namespace std;
ll a,b,n,s;
void solve()
{
	sf(a)sf(b)sf(n)sf(s);
	ll x=s/n;
	ll fir=x*n;
	if(x<=a && s-fir<=b)
	{
		puts("YES");
		return;
	}
	else if(s-fir>b)
	{
		puts("NO");
		return;
	}
	else if(x>a)
	{
		if(s-a*n>b)
		{
			puts("NO");
			return;
		}
		else
		{
			puts("YES");
			return;
		}
	}
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
}

B

思路

要让字典序最小,就要把小的数字尽量交换到前面。题目给出的是一个序列,无重复元素,因此从小到大枚举数字,能左移就左移,直到前一个数字比当前数字小或者前面的交换机会已经用过。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
typedef long long ll;
using namespace std;
const int N=104;
int n;
int a[N],ava[N];
void solve()
{
	scanf("%d",&n);
	rep(i,1,n)
	{
		scanf("%d",a+i);
	}
	rep(i,1,n)
		ava[i]=1;
	int cnt=0;
	rep(cur,1,n)
	{
		rep(i,1,n)
		{
			if(a[i]!=cur)
				continue;
			int pos=i;
			while(ava[pos-1] && a[pos-1]>a[pos])
			{
				++cnt;
				swap(a[pos-1],a[pos]);
				ava[pos-1]=0;
				--pos;
			}
		}
	}
	rep(i,1,n)
		printf("%d ",a[i]);
	printf("\n");
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
}

C

思路

木板总长度不超过nn,因此可以想象成在木板间插缝。缝的个数最少是m1m-1,最多是m+1m+1,要让缝平均宽度最小(更好跳),取m+1m+1作为缝的个数,则缝的平均宽度为dis=nΣc[i]m+1dis=\frac{n-\Sigma c[i]}{m+1}。因为有可能除不尽有剩余,因此还要计算一下剩余宽度res=(nΣc[i])%(m+1)res=(n-\Sigma c[i])\% (m+1),由该式可知res[0,m]res\in [0,m],因此将前resres个缝的宽度增加11,就可以保证最终没有剩余的空间。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
typedef long long ll;
using namespace std;
const int N=1005;
ll n,m,d,sum;
ll c[N],a[N];
int main()
{
	sf(n)sf(m)sf(d);
	rep(i,1,m)
	{
		sf(c[i]);
		sum+=c[i];
	}
	ll dis=(n-sum)/(m+1);
	ll res=(n-sum)%(m+1);
	if((res && dis+1>d-1) || dis>d-1)
	{
		puts("NO");
		return 0;
	}
	int cur=0;
	for(int i=dis;i<n;i+=dis)
	{
		if(res)
		{
			++i;
			--res;
		}
		int j;
		++cur;
		for(j=i+1;j<=i+c[cur];++j)
			a[j]=cur;
		i=j-1;
	}
	puts("YES");
	rep(i,1,n)
		printf("%lld ",a[i]);
	puts("");
	
}

D

思路

贪心,将第一个00(非前缀00,下同)前面的11尽量右移就可以得到最优结果,每个00前面所有11都可以整体右移一步,那么移动的次数就是前面11的个数。每次遇到00就计算出当前移动的总次数,直到其大于kk。那么前面扫过的非前缀00的个数就是扫描后前缀00增加的个数(因为这些00前面的11都可以在kk步内右移,相当于这个00冒泡到左边去了)。剩下的步数必定小于当前00前面11的个数,因此对于剩下的11暴力右移即可。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
typedef long long ll;
using namespace std;
const int N=1E6+10;
ll n,k,cnt,sum,zero,one,p;
string s;
void print(string cur)
{
	rep(i,1,n)
		printf("%c",cur[i]);
	puts("");
}
void solve()
{
	scanf("%lld%lld",&n,&k);
	cin>>s;
	s=' '+s;
	cnt=sum=zero=one=0;
	rep(i,1,n)
	{
		if(s[i]=='0')
		{
			++zero;
			sum+=one;
			if(sum>k)
			{
				sum-=one;
				p=i-1;
				--zero;
				break;
			}
		}
		else
			++one;
	}
	rep(i,1,zero)
		s[i]='0';
	rep(i,zero+1,zero+one)
		s[i]='1';
	k-=sum;
	for(int i=min(p,n-1);i>zero;--i)
	{
		if(k)
		{
			swap(s[i],s[i+1]);
			--k;
		}
		else
			break;
	}
	print(s);
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
}

E

思路

先将原数组排序,设f[i]f[i]为最后一个元素为第ii个元素时得到的最小代价,通过严谨的数学推理(样例打表)发现,ff数组递增。第ii个元素可以作为以第jj个元素(3ji33\leq j\leq i-3)为结尾的组合中的下一个组合,代价为f[i]=f[j]+a[i]a[j+1]f[i]=f[j]+a[i]-a[j+1];此外,第ii个元素还可以合并到以第i1i-1个元素为结尾的组合中,代价为f[i]=f[i1]+a[i]a[i1]f[i]=f[i-1]+a[i]-a[i-1]。在两者中取最小值即可。
如果暴力枚举jj,那么复杂度是O(n2)O(n^2)。但是可以发现每次取的都是f[j]a[j+1]f[j]-a[j+1]的最小值,那么在枚举ii的之后直接维护一下这个最小值即可,复杂度降为O(n)O(n)

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
#define pii pair<ll,ll> 
typedef long long ll;
using namespace std;
const int N=2E5+10;
ll n,pos,tot;
ll f[N],pre[N],ans[N];
struct Node{
	ll val,p;
}a[N];
void dfs(int cur)
{
	++tot;
	rep(i,pre[cur]+1,cur)
		ans[a[i].p]=tot;
	if(pre[cur])
		dfs(pre[cur]);
}
bool cmpv(const Node &x,const Node &y)
{
	return x.val<y.val;
}
int main()
{
	sf(n)
	rep(i,1,n)
	{
		sf(a[i].val)
		a[i].p=i;
	}
	sort(a+1,a+n+1,cmpv);
	rep(i,4,n)
		f[i]=LONG_LONG_MAX;
	f[3]=a[3].val-a[1].val;
	ll mini=f[3];
	rep(i,4,n)
	{
		if(i-3>=3 && mini>f[i-3]-a[i-2].val)
		{
			pos=i-3;
			mini=f[i-3]-a[i-2].val;
		}
		if(f[i]>mini+a[i].val)
		{
			pre[i]=pos;
			f[i]=mini+a[i].val;
		}
		if(f[i]>f[i-1]+a[i].val-a[i-1].val)
		{
			pre[i]=pre[i-1];
			f[i]=f[i-1]+a[i].val-a[i-1].val;
		}
	}
	dfs(n);
	printf("%lld %lld\n",f[n],tot);
	tot=0;
	rep(i,1,n)
		printf("%lld ",ans[i]);
}

F

思路

首先找规律。如果两个字符串含有的每个字符的数目不同,那么不管怎么操作都不可能一样。还发现如果其中一个字符串(称为AA串,另一个称为BB串)含有两个及以上相同的字母,那么就一定可以把两个相同字母放在一起,然后每次交换BB串中的两个字母,对应地交换AA串中已经放在一起的相同的两个字母,这样就保证每次交换之后,AA串保持不变,BB串不断交换两个相邻字母最终变成AA

由于只要字符串长度超过2626就一定会出现相同的字母,因此所有长度大于2626且每个字母出现次数相等的两个字符串一定可以转化成同一个字符串。对于长度不超过2626的字符串各种暴力都可做。

最后一个阶段除了暴力也有一种复杂度低的算法,考虑将两个字符串都转化为字典序最小的情况,每次只交换相邻的两个字符。记AA串的转化次数为stepastepaBB串的转化次数为stepbstepb。只要abs(stepastepb)abs(stepa-stepb)为偶数即可。假设stepa>stepbstepa>stepb,对于前stepbstepb次操作两者可以同时进行,之后BB串变为目标串。接下来继续对AA串操作stepastepbstepa-stepb次,因为每次操作AA都要对BB造成影响,为了使BB串仍保持字典序最小,可以BB串中的任意选择两个元素进行交换,等下一次交换再把它们换回原先的位置,这样AA串每进行22次交换都可以让BB串中的这两个元素先错位后复原,字典序仍最小。因此只要多余的操作为偶数即可保证两串都可以转变为目标串。

如何求stepastepastepbstepb呢?他们实际上就是原数组逆序对的个数,利用树状数组分别求解即可,复杂度为O(nlogn)O(n\log n)

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
#define lb(x) (x)&(-x)
typedef long long ll;
using namespace std;
const int N=2E5+10;
ll n;
ll a[N],b[N],ta[30],tb[30],t[30];
string s;
void add(ll x)
{
	while(x<=26)
	{
		++t[x];
		x+=lb(x);
	}
}
ll query(ll x)
{
	ll ret=0;
	while(x)
	{
		ret+=t[x];
		x-=lb(x);
	}
	return ret;
}
void solve()
{
	sf(n)
	cin>>s;
	int cf=0,ne=0;
	rep(i,1,n)
		a[i]=b[i]=0;
	rep(i,0,26)
		ta[i]=tb[i]=t[i]=0;
	rep(i,1,n)
	{
		int cur=s[i-1]-'a'+1;
		a[i]=cur;
		++ta[cur];
	}
	cin>>s;
	rep(i,1,n)
	{
		int cur=s[i-1]-'a'+1;
		b[i]=cur;
		++tb[cur];
	}
	rep(i,1,26)
	{
		if(ta[i]!=tb[i])
		{
			ne=1;
			break;
		}
	}
	if(ne)
	{
		puts("NO");
		return;
	}
	rep(i,1,26)
		if(ta[i]>1)
			cf=1;
	if(cf)
	{
		puts("YES");
		return;
	}
	ll stepa=0,stepb=0;
	rep(i,1,n)
	{
		ll cur=a[i];
		stepa+=query(26)-query(cur-1);
		add(cur);
	}
	rep(i,0,26)
		t[i]=0;
	rep(i,1,n)
	{
		ll cur=b[i];
		stepb+=query(26)-query(cur-1);
		add(cur);
	}
	if(abs(stepa-stepb)%2)
		puts("NO");
	else
		puts("YES");
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
}