SDUT 2021 Spring Individual Contest(for 20) - 10(补题+部分题解)

Third-Party Software

Pavel is developing a game. To do that, he needs functions available in a third-party library too famous to be called. It is known that the function i first appeared in version ai and existed until version bi, and starting from the version bi + 1, it is absent in this library.

The library is not free and Pavel needs all the functions. Which minimal number of versions he need to purchase to be able to use all the functions?

Input
The first line contains a single integer n (1 ≤ n ≤ 200000) — the number of the functions.

Each of the next n lines contains two integers ai and bi (1 ≤ ai ≤ bi ≤ 109) — the interval of library versions where function i was available.

Output
In the first line output a single integer k — the minimal number of library versions need to be purchased to unlock all functions.

In the second line output k distinct integers — the numbers of versions need to be purchased.

If there are several possible answers, output any of them.

Example
Input
5
2 4
1 3
2 3
3 6
4 5
Output
2
3 4
题意:就是一个人要玩一个游戏,但功能不全,要买。但是每个功能只在对应的版本中,要想凑齐所有的功能,请问买最少那几个版本?
思路: 把功能对应的版本先排序,以开始时间排成升序(开始相同排结束时间)。把版本区间表示出来,取交集缩小范围(为了少买),最后判断有几个不相交的区间。
秉持着不重不漏从易到难的原则划分一下情况

此时每个功能可能对应着三种情况,就以a和b功能举例说明。
1. 如果a功能的版本时间覆盖了b功能的版本时间,就例如a是2—5,而b是3—4,那么此时当然就是取交集3—4了,3就是可以同时玩两个功能(4也可以,这里取左端点)
2.如果a功能的版本时间,与b功能的版本时间有交集但不是覆盖关系,例如a是3—6,b是4—7,那么此时当然就是取交集4—6,此4就是可以同时玩这两个功能的版本。
3. 如果a功能的版本时间,与b功能的版本时间无交集,完全错开,例如a是3—5,b是6—8,那画一条水平数轴,把版本范围区间画出来,版本有(重叠)交叉范围的,为了少买游戏版本,就缩小区间。最后判断有多少个不想交的区间,表示必买的游戏版本,存入数组里面么这是只能同时买5和6两个版本才能凑齐这两个功能。

下面附上代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200010;
int s[N];
struct node
{
    
    
	int l,r; //代表开始和结束时间 
} a[N];
bool cmp(struct node i,struct node j) //版本区间排序,方便后续的比较大小 
{
    
    
	if(i.l==j.l) return i.r<j.r;
	return i.l<j.l;
}
int main()
{
    
    
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++) scanf("%d%d",&a[i].l,&a[i].r);
	sort(a,a+n,cmp);
	int now=0;  //记录有几个不相交的区间 
	int minn=a[0].l,maxx=a[0].r;  
	for(int i=1;i<n;i++) {
    
    
		if(a[i].l>maxx) {
    
      //这种情况就是两个区间不相交的情况 
			s[now++]=minn;    //记录下a版本的区间,用b版本区间来进行后面的比较 
			minn=a[i].l;    //更新 
			maxx=a[i].r;
		}
		minn=max(minn,a[i].l); //取交集操作 
		maxx=min(maxx,a[i].r);
	}              //这里的版本存的都是左端点 
	s[now++]=minn; //把用来比较的的这个区间存进去 
	printf("%d\n",now); 
	for(int i=0;i<now;i++)
        printf("%d ",s[i]);
     return 0;   
}

Queries on a String

A string s is given. Also there is a string p, and initially it is empty. You need to perform q operations of kind «add a letter to the end of the string p» and «remove a letter from the end of the string p», and after performing each operation you must say whether or not s contains p as a subsequence.

Input
The first line contains the string s of length from 1 to 200000, consisting of lowercase Latin letters.

The second line contains a single integer q (1 ≤ q ≤ 200000) — the number of operations.

Each of the next q lines describes an operation in the format «push c», which means «add letter c to the end of the string p» (c is lowercase Latin letter), or «pop», which means «remove letter from the end of the string p». The «pop» operations is guaranteed never to be applied to the empty string p.

Output
Output q lines, each of which equals «YES» or «NO», depending on whether or not the string p is contained in the string s as a subsequence after performing the corresponding operation.

Example
Input

abcabc
30
push a
pop
push a
push a
push a
pop
push c
push b
pop
pop
push b
push c
push c
pop
pop
pop
pop
push b
push c
push c
pop
push b
push c
pop
pop
push a
push b
push c
push a
pop
Output
YES
YES
YES
YES
NO
YES
YES
NO
YES
YES
YES
YES
NO
YES
YES
YES
YES
YES
YES
YES
YES
YES
YES
YES
YES
YES
YES
YES
NO
YES
题意: 有一个a主字符串,另一个子字符串b是利用“pop""push"操作构造的,每操作一次,就问b是否是a的子序列。
思路: 利用next二维数组来记录主字符串第i个位置后面的但单字符第一次出现的位置,即next(i,j)=x,代表第i个字符后面的’a’+j这个字符在x位置上。
在利用ans[]数组,第i个字符,在主字符串中出现的位置,即ans[i]=j,就表示第i个字符,出现在主字符串的第j个位置。
这样主体框架就构建好了。
对于“push”操作,先看再新加单字符之前是不是主字符串的子序列,如果不是,就直接NO,如果是,在判断新加的单字符能否在主串中找到位置(这时,next数组体现作用),找到位置说明就是子序列,并把ans数组对应位置标记新单字符在主字符串的位置
对于“pop”操作,就看删去一个单字符后,最后一个单字符在主子符串是否找到位置,例如abc,删去c后,就看b能否在主字符串找到位置。
如果不好理解,可以手动把程序走一遍,这样写出来后更好理解(我就是这样干的)
下面附上代码

#include<iostream>
#include<cstring>
#include<algorithm>
const int N=2e5+7;
char s[N];
int nex[N][26];
int pos[26];
int ans[N];
int n;
int main()
{
    
    
	scanf("%s",s+1); //这里+1是为了让字符串中的字符把0位置空出来,从1开始,方便记录位置 
	int len1=strlen(s+1);  
	memset(nex,-1,sizeof(nex)); //格式化为-1 , 
	memset(pos,-1,sizeof(pos));
	for(int i=len1;i>=0;i--) //这里就是记录位置的操作 
	{
    
    
		for(int j=0;j<26;j++)  //这里就是记录第i个字符后面,每个单字符第一次出现的位置 
		{
    
    
			nex[i][j]=pos[j];   //没记录上就代表着该单字符没出现,默认为-1 
		}
		pos[s[i]-'a']=i; //把当前位置字符的位置用pos数组记下来 
	}
	scanf("%d",&n); 
	int len2=0; //副字符串的长度 
	ans[0]=0; //用来记录副字符串的每个单字符,在主子符串的位置的,0就对应主字符串的开头0 
	char order[10]; //读入操作的 
	while(n--)
	{
    
    
		scanf("%s",order);
		if(strcmp(order,"push")==0)
		{
    
    
			char op[3];
			scanf("%s",op);
			if(ans[len2]!=-1&&nex[ans[len2]][op[0]-'a']!=-1) //如果前面的上一个单字符在主子符串中有对应的位置,而且新加上的单字符也有对应的位置,就是子序列 
			{
    
    
				ans[len2+1]=nex[ans[len2]][op[0]-'a']; //并对ans数组进行记录,就是说明副串的第len+1个单字符在主子符串的位置,也是方便后面的访问 
				len2++;   //新来了个单字符,长度+1 
				printf("YES\n");
			}
			else 
			{
    
    
				ans[++len2]=-1; //如果没有对应位置,就标记为-1,就说明新来的单字符在主子符串中没有对应的位置 
				printf("NO\n");    //第一次执行这个操作一定是因为没找到位置,后序的操作可能是因为前面本来不是子序列,所以才标记为-1 
			}
		}
		else     //pop操作 
		{
    
    
			len2--; //长度-1 
			if(ans[len2]==-1) //看删去最后一个单字符后,最后一个单字符是否有对应的位置 
			printf("NO\n");
			else printf("YES\n");
		}
	}
	return 0;
} 

Video Reviews

The studio «Lodka Gaming» is engaged in advertising of their new game «.C.O.N.T.E.S.T: Unexpected Behaviour». The studio’s marketer is planning to communicate with n videobloggers one by one (in the predetermined order, starting from the 1-st and ending with the n-th), offering them to record a video review on the game. All people are different and videobloggers are as well, therefore the i-th videoblogger will record a review in two cases: either he is interested in this game, or there are already at least ai video reviews on this game.

The studio wants to have at least m video reviews in the Internet. The game designer of «Lodka Gaming» understands these video reviews possibly would not appear by themselves, so he wants to convince some video bloggers that they are actually interested in this game. Which minimal number of videobloggers are needed to be convinced?

Input
The first line contains two integers n and m (1 ≤ n ≤ 200000, 1 ≤ m ≤ n) — the number of videobloggers and the required number of video reviews.

The second line contains n integers ai (0 ≤ ai ≤ 200000) — the minimal number of video reviews that should appear in the Internet so that the i-th videoblogger will record a review in case he is not interested in the game.

Output
Output a single integer — the minimal number of videobloggers who have to be convinced to record a video review on the game in order to achieve at least m total video reviews in the Internet.

Examples
Input

7 4
2 1 3 3 4 2 3
Output
1
Input
7 4
2 1 3 3 4 3 2
Output
2
题意: 一家公司想让n位视频博主评论视频,但是从左往右(有顺序), 第i个人会评论这个视频当且仅当前面有a【i】个人评论过了或者他对这个视频感兴趣, 否则他不会评论。 公司想有m个视频评论,问最少要说服多少个人让他们对视频感兴趣, 才能达到目标。(一开始看纯英文版本完全没看懂这是什么意思,必英语阅读理解还难
思路: 二分+贪心。用二分假设要说服mid个人评论视频,看看能否达到目的。如果达到,就缩小说服范围。如果没达到目标就增加说服次数。
二分的目的就是假设说服次数看能否达到要求,用假设的已知代替未知。

而贪心,就是从前往后遍历如果那个人评论,就记下来。如果不评论,就说服他(有说服次数的情况下),从左往右遍历,看看评论数是否达到题目要求。从左往右遍历,看到一个不评论就说服他(在有说服次数的情况下)。
因为说服一个人对评论数贡献为1​,而提早做贡献可以带来更多的贡献(因为是一个一个地找人,前面的贡献对后面有用)
下面附上代码。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200010;
#define ll long long
int a[N];
int n,m;
bool check(int now) //now就是说服次数 
{
    
    
	int ans=0;  //记录评论数 
	for(int i=0;i<n;i++) 
	{
    
    
		if(a[i]>ans&&now>0) //这就是强制说服的情况,在有说服次数的情况下,见到一个说服一个(不满足前面有a[i]个评论数的情况下) 
		{
    
    
			now--; //说服次数-1 
			ans++;  //评论数+1 
		}
		else if(ans>=a[i]) ans++; //如果满足前面有a[i]个评论,那么直接+1评论数 
	}                        //上面两个条件都不符合的情况下,就不记录评论数 
	if(ans>=m) return 1;     //评论数>=需求的m数时,满足条件 
	else return 0;     
}
int main()
{
    
    
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++) {
    
    
		scanf("%d",&a[i]); 
	}
	int l=0,r=m;  //二分模板 
	while(l<r)
	{
    
    
		int mid=(l+r)>>1;
		if(check(mid)) //mid说服条件成立的话,那增加次数肯定也成立,因为是求最少说服次数,所以答案一定在0—mid之间 
		{
    
    
			r=mid;
		}
		else //如果mid说服次数不成立,说明次数太少了,答案在mid+1—m之间 
		{
    
    
			l=mid+1;
		}
	}
	printf("%d\n",l); //跳出循环后,就是恰好的答案 
	return 0;
}

Forgotten Spell

Three mages are trying to remember a spell. Each of them believes the spell is some string of length n and it is known that each of the mages is mistaken in at most one letter. It is required to restore the forgotten spell.

Input
Three strings of the same length n (1 ≤ n ≤ 200000) are given in the input. They consist of lowercase Latin letters.

Output
If such a situation is not possible, output «Impossible».

If there are several spells satisfying the conditions, output «Ambiguous».

Finally, if the forgotten spell is the only one possible, output it.

Examples
Input
aab
aca
daa
Output
aaa
Input
abc
aca
abc
Output
Ambiguous
Input
abcde
fghij
klmno
Output
Impossible

题意: 一个长度为n的字符串,有一个位置的字母不确定是什么。现在有三个复制版本A,B,C,但每个版本最多会有一个错误,要求你还原出原来的字符串是啥。
思路:先利用for循环从头到尾扫一遍,看A,B,C三个版本不一样的位置。
1.假如不一样的位置数量>3,那就无法还原原来的字符串。
2.假如不一样的位置数量=0,那就是有多解,输出Ambiguous
3.假如不一样的位置数量在1—3之间,那就暴力枚举不一样的位置的所有情况,共有26^3的情况。判断当前枚举的情况是否是原来的字符串,如果符合就记数+1,如果符合情况的数量>1,就说明多解,输出Ambiguous。
如果符合情况=1,就是唯一正解。如果符合情况=0,说明无解,直接Impossible。
注意:枚举出的情况,若是正解,说明与A,B,C字符串的区别只差1或0个单字符。
下面附上代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=200010;
char a[N],b[N],c[N];
char ta[N],tb[N],tc[N],t[N];
char ans[N];
vector<int> v;
int n,m,now;
bool check() //判断函数 
{
    
    
	int fa=0,fb=0,fc=0;
	for(int i=0;i<m;i++) //枚举后形成的字符串与A、B、C进行比较 
	{
    
                                
		if(t[i]!=ta[i]) fa++;  //当发现不一样,就计数+1, 
		if(t[i]!=tb[i]) fb++;   
		if(t[i]!=tc[i]) fc++;   //与上面同理 
	}
	if(fa>1||fb>1||fc>1) return 0; //只要有一个字符串与枚举后的字符串不同处>1,就说明是正解字符串 
	return 1; //符合条件 
}
void dfs(int k) //k是指枚举到的位置 
{
    
    
	if(now>1) return ; //如果正解情况>1,说明不确定 
	if(k==m) 枚举后形成的字符串长度到达要求
	{
    
    
		if(check()) //如果是正解之一,就记录下来 
		{
    
    
			now++; //正解数量+1 
			for(int i=0;i<m;i++)
			    ans[v[i]]=t[i]; //往对应位置把正解记录上去 
		}
		return ;
	}
	for(int i='a';i<='z';i++) //从a开始枚举 
	{
    
    
		t[k]=i;
		dfs(k+1); 
	}
}
int main()
{
    
    
	scanf("%s%s%s",a,b,c);
	n=strlen(a);
	m=0;
	v.clear();
	for(int i=0;i<n;i++)
	{
    
    
		if(a[i]==b[i]&&a[i]==c[i]) //如果三个字符都一样,那就记录下来 
		{
    
    
			ans[i]=a[i]; //把一样的字符记下来,如果有唯一正解,那这个一样的一定是其中一部分 
		}
		else 
	    {
    
    
		    ta[m]=a[i]; //用ta记录a数组不一样的位置,下面同理 
		    tb[m]=b[i];
	     	tc[m]=c[i];
		    m++; //长度+1 
		    v.push_back(i); //把不一样的位置记下来,方便后续枚举的时候,只往不一样的插入 
	    }
	}
	if(v.size()==0)  //没有不同那就是多解 
	{
    
    
		printf("Ambiguous\n");
		return 0;
	}
	if(v.size()>3)  //不同处>3,那就是无解 
	{
    
    
		printf("Impossible\n");
		return 0;
	}
	now=0; //解的数量 
	dfs(0); //从第0号位置开始枚举 
	if(now==0) printf("Impossible\n");
	else if(now>1) printf("Ambiguous\n");
	else printf("%s\n",ans);
	return 0;
}

Restoring Numbers

Pavel had two positive integers a and b. He found their sum s and greatest common divisor g, and forgot a and b after that. Help him to restore the original numbers.

Input
A single line contains two integers s and g (1 ≤ s ≤ 109, 1 ≤ g ≤ 109) — sum and greatest common divisor of the numbers a and b.

Output
If Pavel made a mistake and there are no such numbers a and b, output a single number  - 1.

Otherwise, output two positive integers a and b on a single line, separated by a space. If there are multiple possible solutions, output any of them.

Examples
Input

6 2
Output
4 2
Input
7 2
Output
-1
题意: 有两个数a和b,给你两数的和sum和两数的最大公因子g,让你求a和b,如果没有输出-1.
思路: 我令a是g的倍数,然后b=s-a,就看a和b的最大公约数是否是g就行,如果是就输出,如果循环结束还没找到,就-1。(这种方法时间复杂个人感觉还行,提交时显示31ms

#include<bits/stdc++.h>
using namespace std;
int main()
{
    
    
	int s,g;
	scanf("%d%d",&s,&g);
	int a,b;
	int x=0;
	int flag=0;
	int i;
    for(i=1,a=g;a<s;i++) 
    {
    
    
    	b=s-a;
   	    x=__gcd(a,b);
   	    if(x==g)
   	    {
    
    
   	    	flag=1;
   	    	break;
	    }
	    a=a*i;  //令a是g的倍数,依次往上乘倍数(保证不超过s) 
	}
	if(flag) 
	printf("%d %d\n",b,a);
	else printf("-1\n");
	return 0;
}

更简单的方法
输入的和减去最大公因子,如果是最大公因子的倍数就有原始的数,不然就没有。(写几个数找一下规律就行),但要注意s==g的情况,肯定是-1.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
    
    
	ll s,g;
	scanf("%lld%lld",&s,&g);
	if((s-g)%g==0&&s!=g)
	{
    
    
		printf("%d %d",s-g,g);
	}
	else printf("-1\n");
} 

Minimal Area

You are given a strictly convex polygon. Find the minimal possible area of non-degenerate triangle whose vertices are the vertices of the polygon.

Input
The first line contains a single integer n (3 ≤ n ≤ 200000) — the number of polygon vertices.

Each of the next n lines contains two integers xi and yi ( - 109 ≤ xi, yi ≤ 109) — the coordinates of polygon vertices.

The polygon is guaranteed to be strictly convex. Vertices are given in the counterclockwise order.

Output
It is known that the area of triangle whose vertices are the integer points on the grid is either integer or half-integer.

Output a single integer — the required area, multiplied by 2.

Examples
Input

4
0 1
3 0
3 3
-1 3
Output
5
Input
3
0 0
1 0
0 1
Output
1
Input
4
-999999991 999999992
-999999993 -999999994
999999995 -999999996
999999997 999999998
Output
3999999948000000156
Note
It is recommended to make all calculations using integer numbers, because floating point precision most likely would not be enough.
题意: 给你一个n个点的凸多边形,给了你n个坐标。让你求出构成的三角形的最小面积。
输出这个三角形面积的二倍
思路: 直接枚举相邻三个点构成的三角形的面积,然后取最小值。唯一要注意的点就是因为给的都是坐标,求三角形面积要用到叉乘公式(虽说我一开始不知道这个名字,只是高中时,知道有这么个公式
为什么要取相邻三个点的一一定构成最小面积,这个证明请看一位大佬的证明,这里就不多赘述。
这是大佬的链接

https://blog.csdn.net/messywind/article/details/115271972

已知A(x1,y1),B(x2,y2),C(x3,y3)三点
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=200010;
#define inf 9223372036854775807 
#define ll long long  //数据太大,开long long 
int n;
ll a[N],b[N];
int main()
{
    
    
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
    
    
		scanf("%lld %lld",&a[i],&b[i]);
	}
	ll s;
	ll res=inf;
	for(int i=0;i<n;i++)  //这里用了取余,是为方便取相邻的三个点 
	{
    
    
		ll x1=a[i]-a[(i+1)%n],x2=a[i]-a[(i+2)%n];
		ll y1=b[i]-b[(i+1)%n],y2=b[i]-b[(i+2)%n];
		s=abs(x1*y2-x2*y1);
		res=min(res,s);
	}
	printf("%lld\n",res);
	return 0;
}

Substring Reverse

Two strings s and t of the same length are given. Determine whether it is possible to make t from s using exactly one reverse of some its substring.

Input
The first line contains the string s, and the second — the string t. Both strings have the same length from 1 to 200000 characters and consist of lowercase Latin letters.

Output
Output «YES», if it is possible to reverse some substring of s to make s equal to t, and «NO», otherwise.

Examples
Input

abcdefg
abedcfg
Output
YES
Input
abcdefg
abdecfg
Output
NO
题意:给你一个s串和t串,如果把他们之中的子串进行翻转后,两个字符串相等的话,就输出YES,否则就是NO。
思路: 直接扫一遍,从左往右找第一个字符不一样的就是左区间,再从右往左找第一个出现的不一样的字符右区间,然后看这个区间内的两个字符串的左右区间旋转过来后是不是一样的就行。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200010;
char a[N],b[N];
char c[N],d[N];
int main()
{
    
    
	cin>>a>>b;
	int f=0;
	int q=0,p=1;
	int len=strlen(a);
	for(int i=0;i<len;i++)
	{
    
    
	     if(a[i]!=b[i]||q!=0)
	     {
    
    
	     	c[q]=a[i];
	     	d[q]=b[i];
	     	q++;
		 }
	}
	for(int i=q-1;i>=0;i--)
	{
    
    
		if(c[i]==d[i])
		{
    
    
			c[i]=0;
			d[i]=0;
			q--;
		}
		else break;
	}
	int j=0;
	for(j=0;j<q;j++)
	{
    
    
		if(c[j]==d[q-j-1])
		{
    
    
			continue;
		}
		else break;
	}
	if(j==q) printf("YES\n");
	else printf("NO\n");
}

Parallelograms

There are n sticks, the i-th of which has length ai. Alex wants to assemble from them as many parallelograms as possible simultaneously, with each stick used at most in one parallelogram. What maximal number of parallelograms is it possible to assemble?

Input
The first line contains a single integer n (1 ≤ n ≤ 200000) — the number of sticks.

The second line contains n integers ai (1 ≤ ai ≤ 200000) — the lengths of sticks.

Output
Output a single integer — the maximal number of parallelograms that is possible to assemble.

Examples
Input

4
1 2 1 2
Output
1
Input
12
1 3 5 7 1 3 5 7 1 3 5 7
Output
2
题意: 给出n个长度分别为a1,…,an的木棍,问这些木棍可以组成多少个平行四边形
思路: 记录每条边出现的次数,只要次数>=2,就可以拿出两根形成对边,每形成一条对边,res++,最后输出res/2即可。

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N=200010;
int a[N];
int main()
{
    
    
	int n,x;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
    
    
		scanf("%d",&x);
		a[x]++;
	}
	int res=0;
	for(int i=1;i<=200000;i++) {
    
    
		if(a[i]==0||a[i]==1) 
		continue;
		if(a[i]>=2)
		{
    
    
		    res++;
		    a[i]=a[i]-2;
		    i--;
		}
	}
	printf("%d\n",res/2);
}

如果你有任何建议或者批评和补充,请留言指出,不胜感激。

猜你喜欢

转载自blog.csdn.net/weixin_54699118/article/details/115270146