23.10.29周报

23日第一题:

题意:给一串由0,1,2组成的字符串,0,1和1,2可换位,2和0不可换位,输出其最小字典序排列。

思路:2和0的位置不变,可发现1是换到哪都可以的,于是在第一个2之前的0后输出。这里我用的vector来存0和2,因为没有考虑到无0,2的情况,访问ve数组re了两发。

第二题:一道臭不可闻的模拟。

第三题:

题意:给一字符串,每次可交换任意相邻字母,问将其reverse所需最小操作数。

不会,上网看了下,题解说是和逆序对有关,遂看了下逆序对,有三种算法,暴力,归并排序,树状数组,所以顺便把归并排序和树状数组看了下。

归并排序求逆序对,时间复杂度为O(nlogn),在求逆序对的同时将数组排序(倒过来说也可以),代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n;
int a[101],tmp[101];
int ans;
void pai(int l,int mid,int r){
	int idx=l;
	int i=l,j=mid+1;
	while(i<=mid && j<=r){
		if(a[i]<=a[j]) tmp[idx++]=a[i++];
		else{
			ans+=mid-i+1;//逆序对
			tmp[idx++]=a[j++];
		}
	}
	while(i<=mid) tmp[idx++]=a[i++];
	while(j<=r) tmp[idx++]=a[j++];
	for(int i=l;i<=r;i++) a[i]=tmp[i];
}
void gb(int l,int r){
	if(l>=r) return ;
	int mid=l+(r-l)/2;
	gb(l,mid);
	gb(mid+1,r);
	pai(l,mid,r);
}
void solve(){
	cin >> n;
	for(int i=1;i<=n;i++) cin >> a[i];
	gb(1,n);
	for(int i=1;i<=n;i++) cout << a[i] << " ";
	cout << endl << ans;//逆序对
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count

树状数组求逆序对的思路也很巧妙,且让我对树状数组的理解更深刻。时间复杂度为O(nlogn),如没有排序的需求,可用此方法。代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n;
int a[101],t[101];
int ans;
int lowbit(int x){
	return x&-x;
}
void add(int x,int v){
	for(int i=x;i<=n;i+=lowbit(i)) t[i]+=v;
}
int ask(int x){
	int sum=0;
	for(int i=x;i;i-=lowbit(i)) sum+=t[i];
	return sum;
}
void solve(){
	cin >> n;
	for(int i=1;i<=n;i++){
		int tmp;
		cin >> tmp;
		add(tmp,1);
		ans+=i-ask(tmp);//i代表输入了多少数,ask代表输入的数中比tmp小的有多少
	}
	cout << ans;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count

说回正题,思路:啃了会题解,发现做法其实相当于给降序序列求逆序对,如abcde,可生成54321求逆序对,当遇到相同字母,其值逆序排放,如abade,生成34521求逆序对,abada,生成14325求逆序对。虽然还是不懂怎么推导的,但起码翻译成这样就可以写代码了。

代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n,a[N],tmp[N];
ll ans;//第二次,wa
string s;
vector<int>ve[30];
void pai(int l,int mid,int r){
	int i=l,j=mid+1,idx=l;
	while(i<=mid && j<=r){
		if(a[i]<=a[j]) tmp[idx++]=a[i++];
		else ans+=mid-i+1,tmp[idx++]=a[j++];
	}
	while(i<=mid) tmp[idx++]=a[i++];
	while(j<=r) tmp[idx++]=a[j++];
	for(int k=l;k<=r;k++) a[k]=tmp[k];
}
void gb(int l,int r){
	if(l>=r) return ;//第一次,mle
	int mid=l+(r-l)/2;
	gb(l,mid);
	gb(mid+1,r);
	pai(l,mid,r);
}
void solve(){
	cin >> n >> s;
	s=" "+s;
	for(int i=1;i<=n;i++) ve[s[i]-'a'+1].push_back(i);
	for(int i=1;i<=26;i++)
		for(int j=0;j<ve[i].size();j++)
			a[ve[i][j]]=ve[i][ve[i].size()-j-1];
	for(int i=1;i<=n;i++) a[i]=n-a[i]+1;
	gb(1,n);
	cout << ans;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count

然后这里代码写的很粗糙,第一次if(l>=r) return; 我没加等号,直接无限递归mle了,第二次我没给ans开long long导致wa了。已经因为没开llwa了好多次了,以后得记着记录ans的变量直接开long long别犹豫。

27日,周五:

补道周二比赛的题:

题意:给俩整数a和b,一次操作中,可让a/=c(a需能被c整除),或让b/=c(b需能被c整除),问能否通过刚好k次操作让a和b相等.

思路:先考虑最小操作次数minn,如a==b,minn为0,如a!=b但其一是另一因数,minn为1,如a!=b且互不能整除,minn为2(让a/=a,再让b/=b).再考虑最大操作次数maxx,即a能被多少质因数分解加上b能被多少质因数分解。那么理论上只要k在minn和maxx的闭区间范围内,就是YES,否则NO。那么如何计算a和b的质因数的数量呢?不会。去网上看了下,代码如下(函数,效果为输出所有质因数):

void di(int x){
	for(int i=2;i<=x/i;i++){
		while(x%i==0){
			cout << i << endl;
			x/=i;
		}
	}
	if(x>1) cout << x;
}

最后代码写出来tle了,目前没想到什么办法。 

每日三题A题:

题意:只在序列中出现过一次的数是好数,把序列拆成A,B俩序列使A,B序列中好数的数量相等。

思路:记录下原序列中的好数num1,和出现次数大于等于3的数num2,当且仅当num1为奇数且num2为0时输出NO,否则YES。但代码思路出了点问题,写的很冗长复杂。

代码(奇丑无比)如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
struct nod{
	int num,id;
}s[104];
int n;
int num1,num2;
vector<int>ve[3];
bool cmp1(nod a,nod b){
	return a.num!=b.num?a.num<b.num:a.id<b.id;
}
bool cmp2(nod a,nod b){
	return a.id<b.id;
}
void solve(){
	cin >> n;
	for(int i=1;i<=n;i++) cin >> s[i].num,s[i].id=i;
	sort(s+1,s+n+1,cmp1);
	for(int i=1,cnt=1;i<=n;i++){
		if(i==n){
			if(cnt==1) num1++,ve[1].push_back(s[i].id);
			else if(cnt>2) num2++,ve[2].push_back(s[i].id);
			break;
		}
		if(s[i+1].num!=s[i].num && cnt==1) cnt=1,num1++,ve[1].push_back(s[i].id);
		else if(s[i+1].num!=s[i].num && cnt>2) cnt=1,num2++,ve[2].push_back(s[i].id);
		else if(s[i+1].num!=s[i].num) cnt=1;
		else if(s[i+1].num==s[i].num) cnt++;
	}
	sort(s+1,s+n+1,cmp2);
	if(num1&1 && num2==0){cout << "NO"; return ;}
	cout << "YES" << endl;
	if(num1==0) for(int i=1;i<=n;i++) cout << 'A';
	else if(!(num1&1)){
		int idx=0;
		for(int i=1;i<=n;i++){
			if(find(ve[1].begin(),ve[1].end(),s[i].id)!=ve[1].end() && idx<num1/2) cout << 'A',idx++;
			else cout << 'B';
		}
	}else{
		int idx=0;bool iff=0;
		for(int i=1;i<=n;i++){
			if(find(ve[1].begin(),ve[1].end(),s[i].id)!=ve[1].end() && idx<num1/2) cout << 'A',idx++;
			else if(s[i].id==ve[2][0] && !iff) cout << 'A',iff=1;
			else cout << 'B';
		}
	}
	
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count

B题和A题一模一样。

28号周六:

每日三题A题:

题意:朋友打怪,easy怪无伤通,hard怪掉一血,打怪和作弊轮着来,每次打怪可打1或2个怪,每次作弊也可跳过1或2个怪(必须打怪一次,作弊一次)。问通关最少掉多少血。

思路:刚开始想的有点复杂,想不通,后来试着简化思路,不去模拟打怪和作弊的过程,只看掉多少血,纯贪心,就a了。

代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n;
int a[N];
int ans;
void solve(){
	ans=0;
	cin >> n;
	for(int i=1;i<=n;i++) cin >> a[i];
	for(int i=n-1;i;i--){
		if(a[i]) a[i]+=a[i+1];
		else a[i]=0;
	}
	for(int i=1;i<=n;i++){
		if(i==1 && a[i]){ans++; continue;}
		if(a[i]<=2) i+=a[i]?a[i]-1:0;
		else ans+=a[i]/3,i+=a[i]-1;
	}
	cout << ans << endl;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	cin >> t;
	while(t--) solve();
	return 0;
}
//make it count

每日三题B题:

题意:一道很不错的离散化。给n条线段的左右端点,问被i条线段覆盖的点有多少(i从1到n)。

思路:先离散化排序,然后挨个处理每个点,如何处理是关键,开一ans数组存答案(开long long),cnt变量存当前线段数,对于新的点,先把到上个点的距离加进ans【cnt】,再处理端点,如有重合,先处理左再右。对于重复左端点或右端点,得一次把cnt加或减完,再进答案。

(这题好像用差分也可以)

代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n,m;
vector<pair<ll,int>>ve;
ll ans[N];
void solve(){
	cin >> n;
	for(int i=1;i<=n;i++){
		ll l,r;
		cin >> l >> r;
		ve.push_back({l,0});
		ve.push_back({r,1});
	}
	sort(ve.begin(),ve.end());
	int cnt=0;
	for(int i=0,j=ve.size();i<j;i++){
		ans[cnt]+=i==0?0:ve[i].first-ve[i-1].first-1;
		if(!ve[i].second){
			cnt++;
			while(ve[i+1].first==ve[i].first && !ve[i+1].second) i++,cnt++;
		}
		ans[cnt]++;
		if(ve[i].second){
			cnt--;
			while(ve[i+1].first==ve[i].first && ve[i+1].second) i++,cnt--;
		}
	}
	for(int i=1;i<=n;i++) cout << ans[i] << " ";
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count

每日三题C题是道1900的树题,做不是很来。

看看昨天每日三题的C:

题意:一数组的美丽值为其最大子序列和,你可选一子段令其内所有值*=x,问操作一次后的最大美丽值。

思路:一眼dp,但我很不擅长dp,所以得看下题解,顺便练下dp。

看了下题解,看不懂,还是先练下基础dp吧。
看了些基础的dp。

星期天的比赛是华中科技大学新生赛,之前打过的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值