问题 A: 海岸线

  • 题目描述
    一个王国分成n*m个六边形区域,每个区域内是陆地或者是水。如果一条边两侧为陆地和水,则该条边成为海岸线,求这个王国海岸线的长度。

  • 输入
    第一行两个整数N,M。
    以下N行每行M个字符,“.”表示水,“#”表示陆地。偶数行需要向右移半格,具体见样例。

  • 输出
    一个整数,海岸线的长度。

  • 样例输入 Copy
    3 6
    …#.##
    .##.#.
    #.#…

  • 样例输出 Copy
    19

  • 提示
    数据表示如下图像:

    可以看出,海岸线长度为19。

  • 【数据范围】
    对100%的数据1<=N,M<=50。

  • 题意分析:就是把图形转化成六边形,求海岸线长度;

  • 思路分析 :很明显,你不能只考虑上下左右,因为它的两行是错开的

  • 所以要换种思路考虑,如上图,我么可以看到,奇数行都是向左错开,偶数行就是向有错开,我们细心点可一发现<mark>向左错开的相邻的六个分别是上,下,左,右,右上和右下;而向右错开的相邻的六个分别是上,下,左,右,左上和左下</mark>知道这些剩下的就简单多了。

  • 代码

#include<iostream>//线性筛素数
#include<math.h>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#define ll long long int
#define inf 0x3f3f3f3f
const int mod=998244353;
using namespace std;
const int maxn=300006;
 
ll n,q,m,t,p,l,r,sum,c[maxn];
ll a[maxn],b[maxn];
char str[1000][1000];
 
int main () {
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>str[i]+1;
         
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(str[i][j]=='#'){
                if(i%2==0){
                if(str[i][j-1]=='.')
                sum++;
                if(str[i][j+1]=='.')
                sum++;
                if(str[i+1][j]=='.')
                sum++;
                if(str[i-1][j]=='.')
                sum++;
                if(str[i+1][j+1]=='.')
                sum++;
                if(str[i-1][j+1]=='.')
                sum++;
            }
            else {
                if(str[i][j-1]=='.')
                sum++;
                if(str[i][j+1]=='.')
                sum++;
                if(str[i+1][j]=='.')
                sum++;
                if(str[i-1][j]=='.')
                sum++;
                if(str[i+1][j-1]=='.')
                sum++;
                if(str[i-1][j-1]=='.')
                sum++;
            }
        }
        cout<<sum<<endl;
    return 0;
}

问题 B: 最大子序和

题目描述
输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。
例如 1,-3,5,1,-2,3
当m=4时,S=5+1-2+3=7
当m=2或m=3时,S=5+1=6

输入
第一行两个数n,m
第二行有n个数,要求在n个数找到最大子序和
输出
一个数,数出他们的最大子序和

样例输入 Copy
6 4
1 -3 5 1 -2 3
样例输出 Copy
7
提示
30%满足 n,m<=100
50%满足 n,m<=3000
100%满足n,m<=300000

  • 思路分析:
    单调队列
    需要满足两个性质:

  • 队列内具有一定的单调性(优先队列)。
    满足普通队列性质,一端进,另一端出,不可以中间插队。
    但是这样就会现矛盾了,例如一个单调增的队列:1,5,8,9,我们要插入4,这时如果只能从尾端进去的话就打破了其单调性,呢么这时的做法就是从队尾到队头,把大于4的全部T了,然后插入后的队列就变成了1,4。

  • 应用
    常用于优化动态规划(DP)问题。
    代码

#include<iostream>//线性筛素数
#include<math.h>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#define ll long long int
#define inf 0x3f3f3f3f
const int mod=998244353;
using namespace std;
const int maxn=300006;

ll n,q,m,t,p,l,r,sum,c[maxn];
ll a[maxn],b[maxn];

int main () {
	cin>>n>>m;
	b[0]=0;
	for(int i=1; i<=n; i++) {
		cin>>b[i];
		b[i]+=b[i-1];
	}
	l = 1, r = 1, sum= -maxn;
	c[1] = 0;
	for (int i = 1; i <= n; i++) {
		while (l <= r && c[l] < i - m) l++;
		sum = max(sum, b[i] - b[c[l]]);
		while (l <= r && b[c[r]] >= b[i]) r--;
		c[++r] = i;
	}
	cout<<sum<<endl;
	return 0;
}

问题 C: 小猴打架

题目描述
一开始森林里面有N只互不相识的小猴子,它们经常打架,但打架的双方都必须不是好朋友。每次打完架后,打架的双方以及它们的好朋友就会互相认识,成为好朋友。经过N-1次打架之后,整个森林的小猴都会成为好朋友。
现在的问题是,总共有多少种不同的打架过程。
比如当N=3时,就有{1-2,1-3}{1-2,2-3}{1-3,1-2}{1-3,2-3}{2-3,1-2}{2-3,1-3}六种不同的打架过程。

输入
一个整数N。
输出
一行,方案数mod 9999991。
样例输入 Copy
4
样例输出 Copy
96
提示
50%的数据N<=10^3。
100%的数据N<=10^6。

  • 思路分析:
    组合数问题,就是(n-1)!*nn-2;叫啥什么定理
    代码
#include<iostream>//线性筛素数
#include<math.h>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#define ll long long int
#define inf 0x3f3f3f3f
const int mod=998244353;
using namespace std;
const int maxn=300006;

ll n,q,m,t,p,l,r,sum,c[maxn];
ll a[maxn],b[maxn];

int main () {
	cin>>n;
	sum=1;
	for(int i=1;i<=n-2;i++)
		sum=sum*n%9999991;
	for(int i=1;i<=n-1;i++)
		sum=sum*i%9999991;
	cout<<sum<<endl;
	return 0;
}

问题 D: 排队

题目描述
每天,农夫John的N(1 <= N <= 50,000)头牛总是按同一序列排队.有一天,John决定让一些牛们玩一场飞盘比赛.他准备找一群在对列中位置连续的牛来进行比赛.但是为了避免水平悬殊,牛的身高不应该相差太大.
John准备了Q (1 <= Q <= 180,000)个可能的牛的选择和所有牛的身高 (1 <= 身高 <= 1,000,000). 他想知道每一组里面最高和最低的牛的身高差别.
注意: 在最大数据上, 输入和输出将占用大部分运行时间.

输入
第1行: N 和 Q.
第2…N+1行: 第i+1行是第i头牛的身高.
第N+2…N+Q+1行: 每行两个整数A和B(1 <= A <= B <= N), 表示从A到B的所有牛.

输出
第1…Q行: 所有询问的回答 (最高和最低的牛的身高差), 每行一个.
样例输入 Copy
6 3
1
7
3
4
2
5
1 5
4 6
2 2
样例输出 Copy
6
3
0
提示
10%的数据 N,Q<=10
30%的数据 N,Q<=2000

  • 思路分析:
    线段树问题吧,好像还有其他思路没大想出来。就是写了啷个查询函数,一个查最小值,一个查最大值。
    代码:
#include<iostream>//线性筛素数
#include<math.h>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#define ll long long int
#define inf 0x3f3f3f3f
const int mod=998244353;
using namespace std;
const int maxn=300006;

ll n,q,m,t,p,l,r,sum,c[maxn];
ll a[maxn],b[maxn];
char str[1000][1000];

inline int read() {//读入挂 
	int x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}

struct node {
	int l,r,maxx,minn;
} tree[maxn*4];
inline void build(int i,int l,int r) { //递归建树
	tree[i].l=l;
	tree[i].r=r;
	if(l==r) { //如果这个节点是叶子节点
		tree[i].maxx=a[l];
		tree[i].minn=a[l];
		return ;
	}
	int mid=(l+r)>>1;//mid=(l+r)/2;
	build(i*2,l,mid);//分别构造左子树和右子树
	build(i*2+1,mid+1,r);
	tree[i].maxx=max(tree[i*2].maxx,tree[i*2+1].maxx);//刚才我们发现的性质return ;
	tree[i].minn=min(tree[i*2].minn,tree[i*2+1].minn);
}
/* inline void add(int i,int dis,int k) { //dis 为第几位; if(tree[i].l==tree[i].r) { //如果是叶子节点,那么说明找到了 tree[i].maxx=k; tree[i].minn=k; return ; } ll mid=(tree[i].l+tree[i].r)/2; if(dis<=mid) add(i*2,dis,k);//在哪往哪跑 else add(i*2+1,dis,k); tree[i].maxx=max(tree[i*2].maxx,tree[i*2+1].maxx);//返回更新 tree[i].minn=min(tree[i*2].minn,tree[i*2+1].minn); return; } */
inline ll search(ll i,ll l,ll r) { //区间查询
	if(tree[i].l==l && tree[i].r==r)//如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
		return tree[i].maxx;
	ll mid=(tree[i].l+tree[i].r)/2;
	if(r<=mid)  return search(i*2,l,r);//如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
	else if(l>mid) return search(i*2+1,l ,r);
	else return max(search(i*2,l,mid),search(i*2+1,mid+1,r));//如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
}
inline ll search1(ll i,ll l,ll r) { //区间查询
	if(tree[i].l==l && tree[i].r==r)//如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
		return tree[i].minn;
	ll mid=(tree[i].l+tree[i].r)/2;
	if(r<=mid)  return search1(i*2,l,r);//如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
	else if(l>mid) return search1(i*2+1,l ,r);
	else return min(search1(i*2,l,mid),search1(i*2+1,mid+1,r));//如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
}

int main () {
	cin>>n>>m;

	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	
	build(1,1,n);
		
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld",&p,&t);
		printf("%lld\n",search(1,p,t)-search1(1,p,t));
	}
	return 0;
}

问题 I: Right Triangle

题目描述
There is a right triangle ABC with ∠ABC=90°.
Given the lengths of the three sides, |AB|,|BC| and |CA|, find the area of the right triangle ABC.
It is guaranteed that the area of the triangle ABC is an integer.

Constraints
·1≤|AB|,|BC|,|CA|≤100
·All values in input are integers.
·The area of the triangle ABC is an integer.
输入
Input is given from Standard Input in the following format:
|AB| |BC| |CA|

输出
Print the area of the triangle ABC.
样例输入 Copy
3 4 5
样例输出 Copy
6
题意分析:不分析了水题一个
思路分析:略

#include<iostream>//线性筛素数
#include<math.h>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#define ll long long int
#define inf 0x3f3f3f3f
const int mod=998244353;
using namespace std;
const int maxn=2e5+10;

char str[maxn],ss[maxn];
//int isPrime[1e8+10],Prime[maxn];//不能用 int 数组开不出来
ll Prime[maxn];
ll n,q;
ll a[maxn],b[maxn];

int main () {
	for(int i=1;i<=3;i++)
		cin>>a[i];
	sort(a+1,a+1+3);
	cout<<a[1]*a[2]/2<<endl;
	return 0;
}

问题 J: Collatz Problem

题目描述
A sequence a={a1,a2,a3,…} is determined as follows:
The first term s is given as input.
Let f(n) be the following function:
f(n)=n/2 if n is even, and f(n)=3n+1 if n is odd.
ai=s when i=1, and ai=f(ai−1) when i>1.
Find the minimum integer m that satisfies the following condition:
There exists an integer n such that am=an(m>n).
Constraints
·1≤s≤100
·All values in input are integers.
·It is guaranteed that all elements in a and the minimum m that satisfies the condition are at most 1000000.
输入
Input is given from Standard Input in the following format:
s
输出
Print the minimum integer m that satisfies the condition.

样例输入 Copy
8
样例输出 Copy
5
提示
a={8,4,2,1,4,2,1,4,2,1,…}. As a5=a2, the answer is 5.
题意分析:
思路分析:

#include<iostream>//线性筛素数
#include<math.h>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#define ll long long int
#define inf 0x3f3f3f3f
const int mod=998244353;
using namespace std;
const int maxn=2e5+10;

char str[maxn],ss[maxn];
//int isPrime[1e8+10],Prime[maxn];//不能用 int 数组开不出来
ll Prime[maxn];
ll n,q;
ll a[maxn],b[maxn];

int main () {
	cin>>a[1];
	b[a[1]]=1;
	for(int i=2;; i++) {
		if(a[i-1]%2==0) a[i]=a[i-1]/2;
		else a[i]=3*a[i-1]+1;
		b[a[i]]++;
		if(b[a[i]]==2) {
			cout<<i<<endl;
			break;
		}
	}
	return 0;
}

问题 K: Grand Garden

  • 题目描述
    In a flower bed, there are N flowers, numbered 1,2,…,N. Initially, the heights of all flowers are 0. You are given a sequence h={h1,h2,h3,…} as input. You would like to change the height of Flower k to hk for all k (1≤k≤N), by repeating the following “watering” operation:
    Specify integers l and r. Increase the height of Flower x by
    1 for all x such that l≤x≤r.
    Find the minimum number of watering operations required to satisfy the condition.

Constraints
·1≤N≤1000≤hi≤100
·All values in input are integers.
输入
Input is given from Standard Input in the following format:
N
h1 h2 h3 … hN

  • 输出
    Print the minimum number of watering operations required to satisfy the condition.
  • 样例输入 Copy
    4
    1 2 2 1
  • 样例输出 Copy
    2
  • 提示
    The minimum number of watering operations required is 2. One way to achieve it is:
    Perform the operation with (l,r)=(1,3).
    Perform the operation with (l,r)=(2,4).
  • 代码
#include<iostream>//线性筛素数
#include<math.h>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#define ll long long int
#define inf 0x3f3f3f3f
const int mod=998244353;
using namespace std;
const int maxn=300006;

ll n,q,m,t,p,l,r,sum,c[maxn];
ll a[maxn],b[maxn];

int main () {
	cin>>n;
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	m=a[1];
	for(int i=1;i<=n;i++)
	{
		if(i!=n){
			if(a[i]>=m)
			m=a[i];
			else 
			{
				sum+=(m-a[i]);
				m=a[i];
			}
		}
		else {
			if(a[i]>=m)
			 	sum+=a[i];
			else 
				sum+=m;
		}
	}
	cout<<sum<<endl;
	return 0;
}