2022CCPC桂林站部分题解

A. Lily(签到题) 

题目大意:如果一个.在L旁边则不变否则变成C,输出更改后的字符串。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	string s;
	cin>>n;
	cin>>s;
	s=" "+s+" ";
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='L')
		{
		cout<<s[i];
		continue;
	    }
	    if(s[i+1]=='L'||s[i-1]=='L')
	    cout<<'.';
	    else
	    cout<<'C';
	}
	return 0;
}

M. Youth Finale

题目大意:给定一个长度为1-n的排列以及m次操作,输出初始序列冒泡排序交换多少次才能变成顺序数列即有多少逆序对,以及m次操作每一次操作后序列的逆序对。S表示序列左移一位(1 2 3 4 5变为2 3 4 5 1),R代表反转序列。

对于S:当前序列队首为x,那么后面的n-1个数有x-1个数比x小,n-x个数比x大。那么移动到队尾后,x与这n-1个数构成的逆序对变为顺序对,与n-x个数构成的顺序对变为逆序对。所以新的逆序对个数即为上一次的原序列的逆序对个数+(n-x)-(n-1);

对于R:对于一个长度为n的序列,逆序对+顺序对的个数为n*(n-1)/2,序列反转后,原来的逆序对变为顺序对,顺序对变为逆序对,所以反转后的逆序对个数为n*(n-1)/2-当前的逆序对个数。

不难看出无论是S还是R操作,逆序对个数的变化都与队首元素有关,所以我们记录队首元素遍历即可。

对于操作S,如果没反转过,那么操作完后队首元素变为右边一位元素,如果反转后则视为将队尾元素放到队首,所以队首元素为左边一位元素。

对于操作R:如果没反转过,那么其左边的一位元素是上一个移到队尾的,反转后此队尾变为队首。如果反转过,那么其右边的一位元素是上一个移到队尾的。

数据规模较大,用归并排序和树状数组求初始逆序对。

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10; 
long long a[N];
long long c[N];
int n,m;
int l;
long long p=0;
bool flag=0;//还没反转过 
long long  lowbit(long long x)
{
	return x&(-x);
}
void add(long long x)//x位置加1
{
	while(x<=n)
	{
	c[x]++;
	x+=lowbit(x);
    }
} 
long long getsum(long long x)
{
	long long sum=0;
	while(x>0)
	{
	sum+=c[x];
	x-=lowbit(x);
   }
   return sum;
}
int main()
{
	string s;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
	cin>>a[i];
	add(a[i]);
	p+=i-getsum(a[i]);
    }
	cin>>s;
	printf("%lld\n",p);
	l=1;
	long long k=(long long)n*(n-1)/2;
	for(int i=0;i<m;i++)
	{
		if(s[i]=='S')
		{ 
		  	p-=2*a[l]-1-n;
		   printf("%lld",p%10);
		   if(flag)//反转过
		   l--;
		   else
		   l++; 
		   if(l>n)
		   l=1;
		   if(l<1)
		   l=n;
		}
		else
		{
			p=k-p;
			printf("%lld",p%10);
			if(flag)
			l++;//右边一个数是上一个移到头部 
			else
			l--;//左边第一个数是上一个移到尾部 
			if(flag)
			flag=false;
			else
			flag=true;
		   if(l>n)
		   l=1;
		   if(l<1)
		   l=n;
		}
	}
}

E. Draw a triangle

 题目大意:有T个测试样例,每组样例给定x1,y1,x2,y2。输出x3,y3使得三个点组成的三角形面积最小。

 紫色圈内可以用拓展欧几里得算法求解

拓展欧几里得算法:对于整数a,b.必有整数x,y使得ax+by=gcd(x,y).换言之,如果ax+by=m有解,那么m一定是gcd(a,b)的倍数。

Ay-Bx可以用拓展欧几里得算法求出x与y使得Ay-Bx最小即gcd(A,B)

如果A或者B小于0就记录再把结果反转即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int x1,y1,x2,y2,x3,y3;
void exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1,y=0;
        return;
    }
    exgcd(b,a%b,x,y);
    int k=x;
    x=y;
    y=k-a/b*y;
}
void solve()
{
	int fx=1,fy=1;
	cin>>x1>>y1>>x2>>y2;
	int b=(x2-x1),a=-(y2-y1);
	if(a<0)
	{
		fx=-1;
		a=-a;
	}
    if(b<0)
    {
    	fy=-1;
    	b=-b;
    }
    exgcd(a,b,x3,y3);
    x3*=fx;
    y3*=fy;
    x3+=x1;
    y3+=y1;
    cout<<x3<<" "<<y3<<endl;
}
signed main()
{
	int t;
	cin>>t;
	while(t--)
	solve();
}

C. Array Concatenation

 题目大意:给定一个长度为n的序列,m次操作。每次操作有两种:1、原数列复制到后面 2、原数列反转后复制到后面。

 输出m次操作后序列的最大前缀和之和。

对于操作1:

设n倍的数组前缀和为s(n*a1+n*a2+..+n*an),前缀和之和为ss

一开始序列的ss为n*a1+(n-1)*a2+....an,进行一次操作1后变为2n*a1+(2n-1)*a2+....(n+1)*n+n*a1+(n-1)*a2+....an等价于s+2*ss,同理进行四次操作后变为6*s+4*ss;

 将公式整理为下图:

 操作n次为边长为n个网格的三角形中各个网格的值相加

对于操作2:

 所以无论第几次进行操作2,都相当于进行了相同次数的操作1,把一般的ss替换为s'。

观察第一个三角形可知,进行操作一相当于多加了一个当前前缀和之和的三角形再多加一部分s。而且无论什么第几次进行操作二,最终变成的序列都是一半ss一半s',并且进行一次操作二后变为回文序列,此时进行操作一与操作二都相同了。所以,只要进行了操作二,无论进行几次,最终结果是一样的。本质上只有全部进行操作一与不全进行操作一两种前缀和之和。

#include<bits/stdc++.h>
using namespace std;
long long x,ss=0,s=0,su=0;
const int mod=1e9+7;
long long qsm(int a,int b)
{
	if(b==0)
	return 1;
	long long ans=1;
	while(b)
	{
		if(b&1)
		{
			ans=(ans*a)%mod;
		}
		a=(a*a)%mod;
		b>>=1; 
	}
	return ans; 
}
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>x;
		ss=(ss+((n-i+1)*x)%mod)%mod;
		s=(s+(n*x)%mod)%mod;
		su=(su+(i*x)%mod)%mod;
	}
	long long pow1=qsm(2,m-1),pow2=qsm(2,m);
	long long ans1=(((pow2-1)*pow1)%mod*s)%mod;//正方形之和
	long long ans2=(ss*pow2)%mod;//所有的小三角都为ss之和 
	long long ans3=(pow1*(ss+su)%mod)%mod;//所有小三角一半为ss一半为su 
	cout<<max((ans1+ans2)%mod,(ans1+ans3)%mod);
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值