【acm备赛day1】思维专题(A - D)

D题不太会!

目录

A. Fedya and Array - 1100 - spj构造+思维

B. Dora and Search - 1200 - 双指针

C. Matching Numbers - 数学 + 构造 + 找规律

D. Sum on Subarrays - 1500 - spj构造 + 思维


A. Fedya and Array - 1100 - spj构造+思维

Problem - 1793B - Codeforces

题目:

  • maxx是严格大于两边的点,minx是严格小于两边的点,数组两端相连构成环形
  • 需要构造一个长度最短的数组,要求数组之间两个相邻元素的差的绝对值为1且满足maxx和minx的权值总和分别为a,b

思路:

题目给出峰值总和a以及谷值总和b

因为题目是spj,所以往最简单的想

即构造一个峰值和一个谷值,两者权值分别为a和b

则构造出的最短序列长度为(a-b)*2

具体实现:

先从从峰值a开始往谷值b构造出序列【a,a-1,……,b】

再从谷值b向a-1构造  【b+1,b+2,……,a-1】

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <deque>
#define pb push_back
//#define int long long
#define INF 0x3f3f3f3f
#define x first
#define y second
#define all(v) (v).begin(),(v).end()
#define io ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;

void solve()
{
	int maxx,minx;
	cin>>maxx>>minx;
	vector<int> v;
	//既然环内总峰值是maxx,总谷值是minx,则只构造一个峰值和谷值
	int t=maxx;
	while(t>minx) v.pb(t--);
	while(minx<maxx) v.pb(minx++);
	
	cout<<v.size()<<endl;
	for(int x:v) cout<<x<<" ";
	cout<<endl;
} 

signed main()
{
	io;
	int t;
	cin>>t;
	while(t--)
		solve();
	return 0;
}

B. Dora and Search - 1200 - 双指针

Problem - 1793C - Codeforces

题目:

定义数组a为含有n个元素序列,元素值由1~n组成,任意找到一个区间 [l,r]

max 为区间内的最大值,min 为区间内的最小值

要求 al 与 ar ≠ 任意一个区间极值,请任意输出一个答案

思路:

  • 从数组两端向内遍历,最初max值为n,min值为1
  • 如果双指针指到的端点为该区间的极值,则向内移动指针,并更新极值
  • 若遍历完后l==r,说明无满足条件的序列存在
  • 主要是因为序列由1~n组成,则若某端点=极值,当前区间的极值更新只需++或--
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <deque>
#define pb push_back
//#define int long long
#define INF 0x3f3f3f3f
#define x first
#define y second
#define all(v) (v).begin(),(v).end()
#define io ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;

const int N=2*1e5+10;
int a[N];

void solve()
{
	int n;
	cin>>n;
	int maxx=0,minx=n+1;
	for(int i=1;i<=n;i++) cin>>a[i];
	
	int curmin=1,curmax=n; //因为序列元素为 1~n 
	int l=1,r=n;
	//从数组两端开始向内遍历,如果某端不满足条件则删去 
	while(l<r)
	{
		if(a[l]==curmin) l++,curmin++;
		else if(a[l]==curmax) l++,curmax--;
		else if(a[r]==curmin) r--,curmin++;
		else if(a[r]==curmax) r--,curmax--;
		else break;
	}
	if(l==r) cout<<-1<<endl;
	else cout<<l<<" "<<r<<endl; 
} 

signed main()
{
	io;
	int t;
	cin>>t;
	while(t--)
		solve();
	return 0;
}

C. Matching Numbers - 数学 + 构造 + 找规律

Problem - 1788C - Codeforces

题目:

  • 给你一个整数n,则数列为1~2n
  • 现在需要构造n个数对,使其满足数对(a,b)中a和b在数列中
  • 且a1+b1,a2+b2……组成的s1,s2……满足si为公差为1的等差数列
  • 数列中1~2n所有数均要使用完

  • 如果可以构造出满足条件的数列,输出yes,并将数对输出
  • 否则输出no

思路:

由等差数列公式得

        \large \frac{2n(2n+1)}{2}=na_{1}+1\times \frac{n(n-1)}{2}

则化简得

        \large a_{1}=\frac{3n+3}{2}

则构造出的数列,最小值和最大值为:

        \large min=\frac{3n+3}{2}            \large max=min+n-1=\frac{5n+1}{2}

易得n为偶数时无解

关于构造,举两个栗子:

n=3时,1   2   3   ||   4   5   6

1+6=7

2+4=6

3+5=8

n=7时,1   2   3   4   5   6   7   ||  8   9   10   11   12   13   14

2+10=12

4+9=13

6+8=14

1+14=15

3+13=16

5+12=17

7+11=18

综合上述两个栗子,我们可以发现规律——

先让奇数位与末尾进行匹配,再让偶数位接着匹配

设t=n

对【1~n】的奇数位,t-- 依次匹配

对【1~n】的偶数位,t-- 依次匹配

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <deque>
#define pb push_back
//#define int long long
#define INF 0x3f3f3f3f
#define x first
#define y second
#define all(v) (v).begin(),(v).end()
#define PII pair<int,int>
#define io ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;

const int N=2*1e5+10;
int a[N];

void solve()
{
	int n;
	cin>>n;
	if(n%2==0) 
	{
		cout<<"No"<<endl;
		return;
	}
	int t=2*n;
	vector<PII> v;
	for(int i=1;i<=n;i+=2) v.pb({i,t--});
	for(int i=2;i<=n;i+=2) v.pb({i,t--});
	
	cout<<"Yes"<<endl;
	for(auto p:v) cout<<p.x<<" "<<p.y<<endl;
} 

signed main()
{
	io;
	int t;
	cin>>t;
	while(t--)
		solve();
	return 0;
}

D. Sum on Subarrays - 1500 - spj构造 + 思维

Problem - 1809C - Codeforces

题目:

用-1000~1000的数,构造一个长度为n,有k个子序列的元素和为正数,其余(n*(n+1)/2-k)个子序列元素之和为负数的整数序列

思路:

k个正数,我们已知长度为n的序列有n*(n+1)/2个子序列

对于k个整数,我们可以通过构造全正数序列来满足正数子序列需求

构造形如:2,2,2……2,x,-1000,-1000

例如:

n=4,k=5时

2  2  -1  -1000

设k为当前状态仍需要的正数个数

前两个2可提供3(i=1 + i=2 = 3)个正数,还差2个整数,则该x需要保证自身是负数的情况下,与前面的数组合提供2个正数,x=(-1)*2*(i-k)+1

n=7,k=4时

2  2  -3  -1000  -1000  -1000

前两个2可以提供3个正数,还差1个正数,要保证自己是负数的情况下,与前面的数组合只提供1个正数,则为x=(-1)*2*(i-k)+1

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <deque>
#define pb push_back
//#define int long long
#define INF 0x3f3f3f3f
#define x first
#define y second
#define all(v) (v).begin(),(v).end()
#define PII pair<int,int>
#define io ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;

const int N=2*1e5+10;
int a[N];

void solve()
{
	int n,k;
	cin>>n>>k;
	
	for(int i=1;i<=n;i++)
	{
		if(k>=i)
		{
			cout<<2<<" ";
			k-=i;
			continue;
		}
		if(k==0)
		{
			cout<<-1000<<" ";
			continue;
		}
		cout<<-2*(i-k)+1<<" ";
		k=0;
	}
	cout<<endl;
} 

signed main()
{
	io;
	int t;
	cin>>t;
	while(t--)
		solve();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_61639349/article/details/129968671
d1