Codeforces Round #710 (Div. 3)个人题解

Codeforces Round #710 (Div. 3)


在这里插入图片描述

Problem A:Strange Table

给定三个数 n , m , x n,m,x n,m,x ,定义一个 3 ∗ 5 3*5 35 的矩阵的纵向排列为 1 4 7 10 13 2 5 8 11 14 3 6 9 12 15 \begin{matrix} 1 & 4 & 7 & 10 & 13 \\ 2 & 5 & 8 & 11 & 14 \\ 3 & 6 & 9 & 12 & 15 \\ \end{matrix} 123456789101112131415 ,其横向排列是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \begin{matrix} 1 & 2 & 3 & 4 & 5 \\ 6 & 7 & 8 & 9 & 10 \\ 11 & 12 & 13 & 14 & 15 \\ \end{matrix} 161127123813491451015 ,求纵向排列 x x x 的位置在其横向排列矩阵中的对应数字是多少 ?


借鉴计算机中二维数组从0开始的表示方法,将原来从1开始的数组映射到从0开始,统一将 X − − , X--, X, 0 3 6 9 12 1 4 7 10 13 2 5 8 11 14 \begin{matrix} 0 & 3 & 6 & 9 & 12 \\ 1 & 4 & 7 & 10 & 13 \\ 2 & 5 & 8 & 11 & 14 \\ \end{matrix} 01234567891011121314 观察有 x 的 横 纵 坐 标 为 ( x % n , x / n ) , 坐 标 为 ( x , y ) 的 点 在 横 向 排 列 之 后 的 数 字 为 x ∗ m + y x的横纵坐标为(x \%n,x/n) ,坐标为(x,y)的点在横向排列之后的数字为x*m+y x(x%n,x/n),(x,y)xm+y

// Problem: A. Strange Table
// Contest: Codeforces - Codeforces Round #710 (Div. 3)
// URL: https://codeforces.com/contest/1506/problem/A
// Memory Limit: 256 MB
// Time Limit: 2000 ms

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
const int N=2e5+10;
int n,m,_;
int main()
{
	cin>>_;
	while(_--)
	{
		cin>>n>>m>>x;
		x--;
		ll x1,y1;
		x1=x%n,y1=x/n;
		cout<<x1*m+y1+1<<endl;
	}
	return 0;
}

如果不将下标进行映射就会陷入特判中,超级麻烦

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll;
const int N=110;
int n;
/*
1 3
2 4
*/
int main()
{
	int t;cin>>t;
	while(t--)
	{
		long long n,m,x;scanf("%lld%lld%lld",&n,&m,&x);
		int X,Y;
		if(x%n==0)
			X=x/n;
		else
			X=x/n+1;

		Y=x-x/n*n;
		if(Y==0)
			Y=n;
		// Y,X
//		cout<<"Y="<<Y<<" X="<<X<<endl;
		
		if(X==m)
			cout<<Y*m<<endl;
		else
			cout<<(Y-1)*m+X<<endl;
//		cout<<Y<<" "<<X<<endl;
	}
}

和这题类似的一道题

AcWing 1219. 移动距离

给定 w , m , n , w 是 宽 度 , m 和 n 是 两 个 编 号 , 求 两 个 编 号 之 间 的 曼 哈 顿 距 离 w,m,n,w是宽度,m和n是两个编号,求两个编号之间的曼哈顿距离 w,m,n,wmn, 曼哈顿距离的定义:给定两个坐标 ( x 1 , y 1 ) , ( x 2 , y 2 ) , d i s = a b s ( x 1 − x 2 ) + a b s ( y 1 − y 2 ) (x1,y1),(x2,y2),dis=abs(x1-x2)+abs(y1-y2) (x1,y1),(x2,y2),dis=abs(x1x2)+abs(y1y2)

1  2  3  4  5  6
12 11 10 9  8  7
13 14 15 .....

input 
6 8 2
ouput
4

同样将所有下标左移
 0  1  2  3  4  5 
11 10  9  8  7  6
12 13 14 15 16 17

观察有对于移动后的 x , 它 的 下 标 是 ( x / w , x % w ) , 当 x / w 是 奇 数 的 时 候 , x 的 纵 坐 标 = w − 1 − x % w , 直 接 计 算 即 可 x,它的下标是(x/w,x\%w) ,当x/w是奇数的时候,x的纵坐标=w-1-x\%w,直接计算即可 x,(x/w,x%w)x/wx=w1x%w,

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef pair<int,int>PII;
typedef pair<long,long>PLL;


typedef long long ll;
const int N=2e5+10;
int w,n,m,_;
int main()
{
	cin>>w>>n>>m;
	int x1,y1,x2,y2;
	n--,m--;
	//借鉴c++中二维数组的存储方式,tql
	if((n/w)%2==0)
		x1=n/w,y1=n%w;
	else
		x1=n/w,y1=w-1-n%w;
		
	if((m/w)%2==0)
		x2=m/w,y2=m%w;
	else
		x2=m/w,y2=w-1-m%w;
	
	cout<<abs(x1-x2)+abs(y1-y2)<<endl;
	return 0;
}

Problem B:Partial Replacement

给你一个由 .* 组成长为 n n n的字符串和数字 k k k,你需要把串中第一个*和最后一个*替换成字符x,并且两个相邻的字符x 之间的距离不能超过 k k k ,如果你要替换 i , j 这 两 个 字 符 , 距 离 定 义 为 j − i i,j这两个字符,距离定义为j-i i,jji

询问想要达到上述目的的最小替换次数是多少

(题目保证有解)


首先找到第一个*和最后一个*的位置,特判只有一个*的情况,之后以 k 为 间 隔 k为间隔 k 贪心向后查找,最后判断贪心过程中最后一个查到到*的位置是不是一开始的 l a s t last last如果是的话直接输出,否则答案+1再输出


一开始贪心也不对,这题得知道首次出现和最后一次出现的位置,贪心的时候得在第二个for循环里计数,不然很容易错。

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll;
const int N=110;
int n;
int main()
{
	int k,t,n;cin>>t;
	while(t--)
	{
		string s;cin>>n>>k>>s;
		int cnt=1,fir=-1,last=-1;
		/*
		特判只有一个*的
		然后把首填好
		之后在这个区间里尽可能的向后找,直到把尾巴填上 
		*/ 
		for(int i=0;i<n;i++)
			if(s[i]=='*')
				{fir=i;break;}
		for(int i=n-1;i>=0;i--)
			if(s[i]=='*')
				{last=i;break;}
		if(fir==last)
		{
			cout<<1<<endl;
			continue;
		}
		int pos=fir;
		
		int i;
		for(i=fir;i+k<=last;)
		{
			for(int j=i+k;j>i;j--)
			{
				if(s[j]=='*')
				{
					cnt++;
					pos=j;break;
				}
			}
			
			i=pos;
		}
		if(i==last)
			cout<<cnt<<endl;
		else
			cout<<cnt+1<<endl;
	}
}

Problem C:Double-ended Strings

给两个字符串 a , b a,b a,b (不一定等长),每次操作可以删除 a , b a,b a,b 串的第一个或最后一个字符,空串是相等的,问最少进行 多少次操作可以使得 a , b a,b a,b 两个字符串相等


I n p u t Input Input

The first line contains a single integer t   ( 1 ≤ t ≤ 100 ) t\ (1 \le t \le 100) t (1t100). Then tt test cases follow.

The first line of each test case contains the string a   ( 1 ≤ ∣ a ∣ ≤ 20 ) a\ (1 \le |a| \le 20) a (1a20), consisting of lowercase Latin letters.

The second line of each test case contains the string b   ( 1 ≤ ∣ b ∣ ≤ 20 ) b\ (1 \le |b| \le 20) b (1b20), consisting of lowercase Latin letters.

O u t p u t Output Output

For each test case, output the minimum number of operations that can make the strings aa and bb equal.


a n s = a . s i z e ( ) + b . s i z e ( ) − 2 ∗ 两 者 最 长 公 共 子 串 的 长 度 ans=a.size()+b.size()-2*两者最长公共子串的长度 ans=a.size()+b.size()2

O ( n 3 ∗ 20 ) , 类 似 区 间 d p 再 乘 上 配 对 的 时 间 复 杂 度 O(n^3*20),类似区间dp再乘上配对的时间复杂度 O(n320),dp

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll;
const int N=1101;
int n;
/*
对于两个长度不超过20的字符串找到他们的连续最长公共子串的长度
*/
int main()
{
	int t;cin>>t;
	while(t--)
	{
		string s,ss;
		cin>>s>>ss;
		int res=0;
		int len=min(s.size(),ss.size());
// 		debug(len);
		for(int i=1;i<=len;i++)//枚举公共的串长
		{
			// int t=0;
			// debug(i);
			for(int j=0;j+i-1<s.size();j++)//枚举起点
				for(int k=0;k+i-1<ss.size();k++)//枚举起点
				{
					if(s.substr(j,i)==ss.substr(k,i))
					{
						res=max(res,i);
					}
				}
		}
		// debug(res);
		// debug(s.size()+ss.size());
		cout<<s.size()+ss.size()-2*res<<endl;
	}
}

Problem D:Epic Transformation

给你一个数组,可以进行一下操作 0 或 者 多 次 0或者多次 0 ,挑选数组中两个不同的元素,将他们删除。

问,进行上述操作后所得到数组可能的最小的大小是?


法一,类比[NOIP2004 提高组] 合并果子

使用优先队列,下面是合并果子的AC代码

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>	
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
const int N=2e5+10;
int n,m,_,x;
int main()
{
    cin>>n;
    priority_queue<int,vector<int>,greater<int>>que;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        que.push(x);
    }
    int res=0;
    while(que.size()>1)
    {
        int a=que.top();
        que.pop();
        int b=que.top();
        que.pop();
        res+=a+b;
        que.push(a+b);
    }
    cout<<res;
	return 0;
}
顺便小结一下priority_queue的使用方法
priority_queue<int>que,默认大根堆
priority_queue<int,vector<int>,greater<int>>que,小根堆
	size()
	empty()
	push()//压入一个元素
	top()//返回堆顶元素
	pop()弹出堆顶元素
遍历方式,不用for(auto c:que)的方式进行遍历,不能使用迭代器遍历
可以用while(que.size())
{
	cout<<que.top<<" ";
	que.pop();
}遍历

本题代码

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>	
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef pair<int,int>PII;
typedef pair<long,long>PLL;


typedef long long ll;
const int N=2e5+10;
int n,m,_,x;
int main()
{
    cin>>_;
    while(_--)
    {
        scanf("%d",&n);
        map<int,int>mp;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            mp[x]++;
        }
        priority_queue<int>que;//使用大根堆
        for(auto c:mp)
            que.push(c.second);
        
        while(que.size()>1)
        {
            int a=que.top();
            que.pop();
            int b=que.top();
            que.pop();
            a--,b--;
            if(a)
                que.push(a);
            if(b)
                que.push(b);
        }
        if(que.empty())puts("0");
        else
            printf("%d\n",que.top());
    
    }
	
	return 0;
}
法二,参考滑稽的代码 如果 $n $ 是奇数的话, r e s res res 至少是1,否则统计众数出现的次数 n u m num num

$ res= max(1,num-(n-num))$ ,看除了众数之外的其他数出现的次数,如果其他数出现的次数大于众数,$ res=0 否则 res= num-(n-num)$

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef pair<int,int>PII;
typedef pair<long,long>PLL;


typedef long long ll;
const int N=2e5+10;
int n,m,_,x;
int main()
{
	cin>>_;
	while(_--)
	{
		scanf("%d",&n);
		map<int,int>mp;
		int cnt=-1;
		for(int i=0;i<n;i++)
		{
			scanf("%d",&x);
			mp[x]++;
			cnt=max(cnt,mp[x]);
		}
		if(n&1)
			printf("%d\n",max(1,cnt-(n-cnt)));
		else
			printf("%d\n",max(0,cnt-(n-cnt)));
	}
	return 0;
}
法三,还有一个是tiger的代码,暂时不想懂,贴个链接tiger#710div3

ProblemE:Epic Transformation

给一个排列 P P P 对应的一个前缀最大值的序列 q q q,如 p = [ 3 , 2 , 4 , 1 , 7 , 5 , 6 ] − > q = [ 3 , 3 , 4 , 4 , 7 , 7 , 7 ] p=[3, 2, 4, 1, 7, 5, 6]->q=[3, 3, 4, 4, 7, 7, 7] p=[3,2,4,1,7,5,6]>q=[3,3,4,4,7,7,7]

q q q 序列对应的所有原序列 p p p 的最小字典序序列和最大字典序是多少. $ n \ (1 \le n\le2 \cdot 10^5)$


容易发现如果一个数第一次在 p p p 中出现,那么无论是最小或者最大字典序这个数字都是确定的,那么我们确定其他位置的数字就好,我使用了set存储所有没有出现过的数字,然后再输出 a n s ans ans 数组的同时遍历了set,最大字典序是使用了 l a s t last last 记录上一个字符,第一个字符肯定会赋给 l a s t last last 的,然后再set中二分查找第一个< l a s t last last 的数字,即使用 l = l o w e r _ b o u n d ( s . b e g i n ( ) , s . e n d ( ) , x ) l=lower\_bound(s.begin(),s.end(),x) l=lower_bound(s.begin(),s.end(),x) 再 $l-- $ 即可.


常数太大了,T在了第3的点上 哭晕

// Problem: E. Restoring the Permutation
// Contest: Codeforces - Codeforces Round #710 (Div. 3)
// URL: https://codeforces.com/contest/1506/problem/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
const int N=2e5+10;
int n,m,_;
bool st[N];
int a[N];
int ans[N];
int main()
{
	cin>>_;
	while(_--)
	{
		scanf("%d",&n);
		set<int>S;
		for(int i=1;i<=n;i++)
			st[i]=0,ans[i]=0;
		// mem(st,0);
		// mem(ans,0);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(!st[a[i]])
				ans[i]=a[i],st[a[i]]=1;
		}
		
		for(int i=1;i<=n;i++)
		{
			if(!st[i])
				S.insert(i);
		}
		set<int>::iterator p=S.begin();
		
		// for(auto t:S)
			// cout<<t<<" ";
		// puts("");
		
		for(int i=1;i<=n;i++)
		{
			if(ans[i])
				printf("%d ",ans[i]);
			else
				printf("%d ",*p++);
		}
		int last=0;
		puts("");
		for(int i=1;i<=n;i++)
		{
			// debug(last);
			if(ans[i])
				printf("%d ",ans[i]),last=ans[i];
			else
			{
				//在set的所有元素中找到第一个小于last的元素
				auto l=lower_bound(S.begin(),S.end(),last);
				l--;
				printf("%d ",*l);
				S.erase(*l);
			}
		}
		puts("");
	}
	return 0;
}

考虑用空间换时间?

参考了tiger2005的方法,将我上个思路中把所有值全存好再取出来,变成一边读数字,一边写答案

当 A [ i ]   ! = A [ i + 1 ] 时 , a n s [ i ] [ 0 ] = a n s [ i ] [ 1 ] = A [ i + 1 ] , 然 后 将 A [ i ] + 1 ∼ A [ i ] − 1 的 数 字 放 到 两 个 s e t 中 , 如 果 A [ i ] = = A [ i + 1 ] , 对 于 最 小 字 典 序 那 么 从 s e t 中 取 出 当 前 的 最 小 元 素 , 最 大 字 典 序 取 出 s e t 中 的 尾 部 最 大 值 。 当A[i]\ !=A[i+1]时,ans[i][0]=ans[i][1]=A[i+1],然后将A[i]+1 \sim A[i]-1的数字放到两个set中,\\如果A[i]==A[i+1],对于最小字典序那么从set中取出当前的最小元素,最大字典序取出set中的尾部最大值。 A[i] !=A[i+1],ans[i][0]=ans[i][1]=A[i+1],A[i]+1A[i]1set,A[i]==A[i+1],set,set

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
#include<map>
#include<set>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef long long LL;
const int N = 200010;
int n;
int a[N];
int ans[N][2];
set<int>st[2];

int main()
{
    int _;
    scanf("%d", &_);
    while(_ --)
    {
        scanf("%d",&n);
		st[1].clear(),st[0].clear();
        int last=0;
        for(int i=1;i<=n;i++)
        {
        	scanf("%d",&a[i]);
        	if(a[i]!=last)
        	{
        		ans[i][0]=a[i];
        		ans[i][1]=a[i];
				int t=a[i];
        		for(int i=last+1;i<t;i++)
        		{
//        			cout<<i<<endl;
					st[0].insert(i);st[1].insert(i);
				}
        		last=a[i];
//        		debug(last);
			}
			else
			{
				ans[i][0]=*st[0].begin();
				st[0].erase(ans[i][0]);
				
				set<int>::iterator it=st[1].end();
				it--;
				ans[i][1]=*it;
				st[1].erase(*it);
			}
		}
//		for(auto c:st[0])
//			cout<<c<<" ";
//		cout<<endl;
        for(int i=1;i<=n;i++)
        	printf("%d ",ans[i][0]);
        puts("");
        for(int i=1;i<=n;i++)
        	printf("%d ",ans[i][1]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值