Educational Codeforces Round 99 (Rated for Div. 2) CF1455A-G 题解

A.
题目大意:
f ( x ) f(x) f(x)等于把 x x x颠倒后的数,给定 n n n,对于任意 0 < x < = n 0<x<=n 0<x<=n,求 x f ( f ( x ) ) \frac{x}{f(f(x))} f(f(x))x有多少个不同的值。
n < = 1 0 100 , T < = 100 n<=10^{100},T<=100 n<=10100,T<=100

分析:答案就是 n n n的位数,十分显然。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>

const int N=107;

using namespace std;

int T;
char s[N];

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%s",s+1);
		int ans=strlen(s+1);
		printf("%d\n",ans);
	}
 } 

B.
题目大意:
开始时在 0 0 0点,第 i i i步可以选择跳到 ( x + i ) (x+i) (x+i)或者 ( x − 1 ) (x-1) (x1),问跳到 n n n最小需要多少步。
n < = 1 0 6 , T < = 1000 n<=10^6,T<=1000 n<=106,T<=1000

分析:
n = k ∗ ( k + 1 ) / 2 + d n=k*(k+1)/2+d n=k(k+1)/2+d,且 d < = k d<=k d<=k
如果 d = 0 d=0 d=0,需要 k k k步;
如果我们在第 i i i步往后跳一步在往前跳一步,此时还是前进了距离 i i i,但是后续的每一步长度都能增加 1 1 1。如果我们刚开始就这么做,最多增加 k − 1 k-1 k1的距离。
所以当 0 < d < k 0<d<k 0<d<k,需要 k + 1 k+1 k+1步;
如果 d = k d=k d=k,需要跳 k + 1 k+1 k+1步再往回跳一步,需要 k + 2 k+2 k+2步。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>

const int maxn=1e6+7; 

using namespace std;

int T,x;
int f[maxn];

int main()
{
	scanf("%d",&T);
	f[0]=0,f[1]=1;
	for (int i=2;i<=2e3;i++)
	{
		int l=i*(i+1)/2-1;
		int r=(i+1)*(i+2)/2-2;
		if (r>1e6) r=1e6;
		for (int j=l;j<=r;j++) f[j]=i+1;
		f[l+1]=i;
		if (r==1e6) break;
	}
	while (T--)
	{
		scanf("%d",&x);
		printf("%d\n",f[x]);
	}
}

C.
分析:题目意思比较复杂,结论就是输入 ( x , y ) (x,y) (x,y),输出 ( x − 1 , y ) (x-1,y) (x1,y)
x , y < = 1 0 6 , T < = 1000 x,y<=10^6,T<=1000 x,y<=106,T<=1000

代码:

#include <iostream>
#include <cstdio>

const int maxn=2e5+7;

using namespace std;

int T,x,y; 

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&x,&y);
		printf("%d %d\n",x-1,y);
	}
}

D.
初始有一个序列 a a a和一个数 x x x,每次可以选择一个 a i > x a_i>x ai>x,交换他们,要求使得 a a a有序需要多少步。
n < = 500 , T < = 1000 n<=500,T<=1000 n<=500,T<=1000

分析:
显然 x x x会越变越大,而且无法交换小于等于它的数。
那么小于等于 x x x的数一定从小到大排好而且在前面,如果大于 x x x的数未排好,那么就把 x x x放在小于等于它的数的后一位,并把那个位置的数换出来。此时就变成了一个新的原问题了,重复处理即可。

代码:

#include <iostream>
#include <cstdio>

const int maxn=507;

using namespace std;

int n,T,x,ans;
int a[maxn];

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&n,&x);
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		a[0]=-1;
		ans=0;
		int flag=1;
		
		for (int i=1;i<=n;i++)
		{
			if (a[i]<=x)
			{
				if (a[i]<a[i-1])
				{
					flag=0;
					break;
				}
			}
			else
			{
				int flag1=1;
				for (int j=i+1;j<=n;j++)
				{
					if (a[j]<a[j-1])
					{
						flag1=0;
						break;
					}
				}
				if (flag1) break;
				swap(x,a[i]);
				ans++;
			}
		}
		if (flag) printf("%d\n",ans);
		     else printf("-1\n");
	}
}

E.
题目大意:
给定平面上给定4个点,要求把它们移成一个平行与坐标轴的正方形,要求移动的距离(曼哈顿距离)和最小是多少。
x , y < = 1 0 9 , T < = 1000 x,y<=10^9,T<=1000 x,y<=109,T<=1000

分析:
对正方形的边长进行三分,至于为什么是单峰我也不清楚。
对于半径为 d d d的正方形,显然可以横纵坐标分开算。
对于横坐标(纵坐标类似),假设四个点的横坐标排序后相邻两个的距离分别为 x , y , z x,y,z x,y,z,则
d < = y + z d<=y+z d<=y+z,长方形左下角横坐标选择第二个点横坐标最优,否则长方形右下角横坐标选择第四个点横坐标最优。点之间的对应可以暴力全排列。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long

using namespace std;

int T,l,r,dx,dy,cnt;
LL ans;
LL a[5],b[5],c[5],d[5],e[5],f[5],v[5];
int t[30][5];

void dfs(int x)
{
	if (x>4)
	{
		++cnt;
		for (int i=1;i<=4;i++) t[cnt][i]=a[i];
		return;
	}
	for (int i=1;i<=4;i++)
	{
		if (!v[i])
		{
			v[i]=1;
			a[x]=i;
			dfs(x+1);
			v[i]=0;
		}
	}
}

LL dis(LL a,LL b,LL c,LL d)
{
	return abs(a-c)+abs(b-d);
}

LL calc(LL k)
{
	LL std=1e15,sum;
	if (e[4]-e[2]>=k)
	{
		c[1]=e[2];
		c[2]=e[2]+k;
		c[3]=e[2];
		c[4]=e[2]+k;
	}
	else
	{
		if (k-(e[4]-e[2])<e[2]-e[1])
		{
			c[1]=e[4]-k;
			c[2]=e[4];
			c[3]=e[4]-k;
			c[4]=e[4];
		}
		else
		{
			c[1]=e[1];
			c[2]=e[1]+k;
			c[3]=e[1];
			c[4]=e[1]+k;
		} 
	}
	if (f[4]-f[2]>=k)
	{
		d[1]=f[2];
		d[2]=f[2];
		d[3]=f[2]+k;
		d[4]=f[2]+k;
	}
	else
	{
		if (k-(f[4]-f[2])<f[2]-f[1])
		{
			d[1]=f[4]-k;
			d[2]=f[4]-k;
			d[3]=f[4];
			d[4]=f[4];
		}
		else
		{
			d[1]=f[1];
			d[2]=f[1];
			d[3]=f[1]+k;
			d[4]=f[1]+k;
		} 
	}
	for (int i=1;i<=cnt;i++)
	{
		sum=0;
		for (int j=1;j<=4;j++) sum+=dis(a[j],b[j],c[t[i][j]],d[t[i][j]]);
		if (sum<std) std=sum;
	}
	return std;
}

void solve()
{
	ans=1e15;
	l=0,r=1e9;
	while (r-l>=2)
	{
		int len=(r-l+1)/3;
		int mid1=l+len-1,mid2=mid1+len;
		LL p1=calc(mid1),p2=calc(mid2);
		if (p1>p2)
		{
			ans=min(ans,p2);
			l=mid1+1;
		}
		else
		{
			ans=min(ans,p1);
			r=mid2-1;
		}
	}
	for (int i=l;i<=r;i++) ans=min(ans,calc(i));
}

int main()
{	
	dfs(1);
	scanf("%d",&T);
	while (T--)
	{
		
		for (int i=1;i<=4;i++) scanf("%lld%lld",&a[i],&b[i]);
		for (int i=1;i<=4;i++) e[i]=a[i];
		sort(e+1,e+5);
		for (int i=1;i<=4;i++) f[i]=b[i];
		sort(f+1,f+5);
		solve();
		printf("%lld\n",ans);
	}
}

F.
题目大意:看原题。
分析:显然前面的位字典序越小越好,设 b [ i ] b[i] b[i]表示这一位有没有被操作过,考虑第 i i i位,有4种情况。
1. b [ i ] = 0 b[i]=0 b[i]=0,则直接改变当前位,否则当前位不变。
2.如果 b [ i ] = 0 b[i]=0 b[i]=0,使自己字典序变小,否则不变;然后让后一位与当前位交换。显然这种情况,如果后一位是 a a a则比3优,否则比3劣。
3.与后一位交换;如果 b [ i + 1 ] = 0 b[i+1]=0 b[i+1]=0,使换过来的这一位字典序变小,否则换过来的位不变。
4.如果 b [ i ] = 0 b[i]=0 b[i]=0,使自己字典序变小,否则不变;然后使 i + 1 i+1 i+1位与 i + 2 i+2 i+2位交换,再使 i + 2 i+2 i+2位与 i i i位交换。
在上述4中情况中选择字典序尽可能小的一种方案,否则尽可能选靠前的方案,因为同样情况省了操作数。注意因为一定要按原始的顺序一位一位处理,所以参与操作的所有位置 b b b值都要设为 1 1 1。代码中的 0 / 1 0/1 0/1与上述分析相反。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>

const int maxn=507;

using namespace std;

int T;
int n,k;
int a[maxn],b[maxn];
char s[maxn];

int calc(int x,int p)
{
	if (!p) return x;
	if ((x==k) || (x==1)) return 1;
	if (x==1) return 1;
	return x-1;
}

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&n,&k);
		scanf("%s",s+1);
		for (int i=1;i<=n;i++)
		{
			a[i]=s[i]-'a'+1;
			b[i]=1;
		}
		int p,minn;
		for (int i=1;i<=n;i++)
		{
			minn=k+1;
			if (calc(a[i],b[i])<minn)
			{
				minn=calc(a[i],b[i]);
				p=1;
			}
			if ((i<=n-1) && (b[i+1]) && (a[i+1]<minn))
			{
				minn=a[i+1];
				p=2;
			}
			if ((i<=n-1) && (b[i]) && (calc(a[i+1],b[i+1])<minn))
			{
				minn=calc(a[i+1],b[i+1]);
				p=3;
			}
			if ((i<=n-2) && (b[i+1]) && (b[i+2]) && (a[i+2]<minn))
			{
				minn=a[i+2];
				p=4;
			}
			printf("%c",'a'+minn-1);
			if (p==1) b[i]=0;
			if (p==2)
			{
				swap(a[i],a[i+1]);
				swap(b[i],b[i+1]);
				a[i+1]=calc(a[i+1],b[i+1]);
				b[i]=b[i+1]=0;
			}
			if (p==3)
			{
				swap(a[i],a[i+1]);
				b[i]=b[i+1]=0; 
			}
			if (p==4)
			{
				swap(a[i+1],a[i+2]);
				swap(b[i+1],b[i+2]);
				swap(a[i],a[i+1]);
				swap(b[i],b[i+1]);
				a[i+1]=calc(a[i+1],b[i+1]);
				b[i]=b[i+1]=b[i+2]=0;
			}
		}
		printf("\n");
	}	
}

G.
题目:看原题。
分析:可以设 f [ i ] [ j ] f[i][j] f[i][j]表示执行到第 i i i行当前值为 j j j的答案。
如果第 i i i条语句是赋值为 x x x,那么
f [ i ] [ x ] = min ⁡ k = 1 m f [ i ] [ k ] f[i][x]=\min_{k=1}^{m}f[i][k] f[i][x]=mink=1mf[i][k]
f [ i ] [ j ] = f [ i − 1 ] [ j ] + V , j ≠ x f[i][j]=f[i-1][j]+V,j≠x f[i][j]=f[i1][j]+V,j=x

对于一个一个 i f if if语句,相当于一个新的类似于上面的dp,不过dp的初值不同。具体来说,程序开头的初值是 f [ 0 ] [ 0 ] = 0 f[0][0]=0 f[0][0]=0,其余为 i n f inf inf。假设 i f if if对应的数为 x x x,则初值为 f [ i ] [ x ] = f [ i − 1 ] [ x ] f[i][x]=f[i-1][x] f[i][x]=f[i1][x],其余为 i n f inf inf
考虑 i f if if语句处理出来的结果与前面的结果合并,假设if语句开始的行为 p p p,结束的行为 q q q
那么
f [ q ] [ j ] = m i n ( f [ p − 1 ] [ j ] , f [ q − 1 ] [ j ] ) , j ≠ x f[q][j]=min(f[p-1][j],f[q-1][j]),j≠x f[q][j]=min(f[p1][j],f[q1][j]),j=x
就是可以选择跳过 i f if if,或者不跳过 i f if if
f [ q ] [ x ] = f [ q − 1 ] [ x ] f[q][x]=f[q-1][x] f[q][x]=f[q1][x]
x x x结束x不可能跳过 i f if if

我们注意到,对于每一行的语句而言,其拥有的状态并不多,而且 i f if if语句在执行后,这些状态就没有用了。所以我们考虑用set来维护状态。具体操作包括整体加 V V V,查找最小,单点修改,以及set合并,这里需要使用启发式合并进行处理。

代码:

#include <iostream>
#include <cstdio>
#include <set>
#include <map>
#define LL long long

const int maxn=2e5+7;

using namespace std;

int n,m,k,cnt;
char str[10];
LL val[maxn];

struct node{
	int op,x;
	LL v;
}a[maxn];

struct rec{
	int x;
	LL w;
};

set <rec> s[maxn];
map <int,LL> h[maxn];

bool operator <(rec a,rec b)
{
	if (a.w==b.w) return a.x<b.x;
	return a.w<b.w;
}

bool operator ==(rec a,rec b)
{
	return ((a.x==b.x) && (a.w==b.w));
}

int solve(int sta,LL d)
{
	int id=++cnt;
	set <rec> ::iterator it,it1;
	s[id].insert((rec){sta,d});
	h[id][sta]=d;
	val[id]=0;
	while (1)
	{
		k++;
		if (a[k].op==1)
		{
 			if (a[k].x!=m)
			{
				it=s[id].begin();
				rec e=*it;
				it=s[id].lower_bound((rec){a[k].x,h[id][a[k].x]});
				rec f=*it;
				if ((it!=s[id].end()) && (f==(rec){a[k].x,h[id][a[k].x]})) s[id].erase(it);
				s[id].insert((rec){a[k].x,e.w-a[k].v});
				h[id][a[k].x]=e.w-a[k].v;
			}
			val[id]+=a[k].v;
		}
		if (a[k].op==3) return id;
		if (a[k].op==2)
		{
			int p=a[k].x;
			it=s[id].lower_bound((rec){a[k].x,h[id][a[k].x]});
			rec f=*it;
			if ((it!=s[id].end()) && (f==(rec){a[k].x,h[id][a[k].x]}))
			{
				int q=solve(a[k].x,h[id][a[k].x]+val[id]);
				if (s[id].size()>=s[q].size())
				{
					it1=s[q].begin();
					while (it1!=s[q].end())
					{
						rec nw=*it1;
						nw.w+=val[q]-val[id];
						it=s[id].lower_bound((rec){nw.x,h[id][nw.x]});
						rec e=*it;
						if ((it!=s[id].end()) && (e==(rec){nw.x,h[id][nw.x]}))
						{
							if ((e.w>nw.w) || (nw.x==p))
							{
								s[id].erase(it);
								s[id].insert((rec){nw.x,nw.w});
								h[id][nw.x]=nw.w;
							}
						}
						else
						{
							s[id].insert((rec){nw.x,nw.w});
							h[id][nw.x]=nw.w;
						}
						it1++;
					}
				}
				else
				{
					it1=s[id].begin();
					while (it1!=s[id].end())
					{
						rec nw=*it1;
						nw.w+=val[id]-val[q];
						it=s[q].lower_bound((rec){nw.x,h[q][nw.x]});
						rec e=*it;
						if ((it!=s[q].end()) && (e==(rec){nw.x,h[q][nw.x]}))
						{
							if ((e.w>nw.w) && (nw.x!=p))
							{
								s[q].erase(it);
								s[q].insert((rec){nw.x,nw.w});
								h[q][nw.x]=nw.w;
							}
						}
						else
						{
							s[q].insert((rec){nw.x,nw.w});
							h[q][nw.x]=nw.w;
						}
						it1++;
					}
					id=q;
				}
			}
			else
			{				
				int dep=1;
				while (dep)
				{
					k++;
					if (a[k].op==2) dep++;
					if (a[k].op==3) dep--;
				}
			}	
		}
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",str+1);
		if (str[1]=='s')
		{
			a[i].op=1;
			scanf("%d%lld",&a[i].x,&a[i].v);
		}
		if (str[1]=='i')
		{
			a[i].op=2;
			scanf("%d",&a[i].x);
		}
		if (str[1]=='e') a[i].op=3;
	}
	a[++n]=(node){3,0,0};
 	int q=solve(0,0);
 	set <rec> ::iterator it;
 	it=s[q].begin();
 	rec e=*it;
 	printf("%lld",e.w+val[q]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值