3.3~3.9

牛客月赛 111

A

贪心,田鸡的最大的a比齐威王次大的v大,田鸡的次大的a比齐威王最小的v大就能赢

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int v[3],a[3];
	for(int i=0;i<3;i++)
		cin >> v[i];
	for(int i=0;i<3;i++)
		cin >> a[i];
	sort(v,v+3);
	sort(a,a+3);
	if(a[2] > v[1] && a[1] > v[0])
		cout << "Yes\n";
	else cout << "No\n"; 
	return 0;
}

B

即使有无限多个且偶数个邪恶英雄合并,也只能得到一个正义英雄,所以贪心,每两个相邻的邪恶英雄合并,就能算出最大的正义英雄

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,sum=0;
	string s;
	cin >> n >> s;
	for(int i=0;i<n;i++){
		if(s[i] == 'y')	sum++;
		else if(s[i] == 'n' && i+1<n && s[i+1] == 'n'){
			sum++;
			i++;
		}
	}
	cout << sum << '\n';
	return 0;
}

C

先滑动窗口求出每个连续相等的区间的连续数目,并给该区间每个元素赋该值;

再将每个非连续区间进行比较,求出最大值

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+1;
vector <string> s(maxn);
int qz[maxn];

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,ans=0;
	cin >> n;
	for(int i=0;i<n;i++)
		cin >> s[i];
	if(n == 1){
		cout << "1\n";
		return 0;	
	}
	else if(n == 2){
		if(s[0] == s[1]) cout << "2\n";
		else cout << "1\n";
		return 0;
	}
	int l=0,r=1,num=1;
	while(1){
		if(r == n){
			for(int i=l;i<r;i++)
				qz[i] = num;
			break;
		}
		if(s[l] == s[r]){
			r++;
			num++;
		}
		else{
			for(int i=l;i<r;i++)
				qz[i] = num;
			num=1;
			l = r;
			r++;
		}
	}
	ans = max(ans,qz[1]);
	ans = max(ans,qz[2]);
	for(int i=2;i<n;i++){
		ans = max(ans,qz[i]);
		if(s[i] == s[i-2] && s[i] != s[i-1])
			ans = max(ans,qz[i-2]+qz[i]);
	}
	cout << ans << '\n';
	return 0;
}

D

DP,这题有墙壁,墙壁的优先级大于自己。到每个点需要的回合是x+y-2,如果x+y-2>=变成墙壁需要的回合数,那么就给这个点权赋极小值,相当于不通了。

注意判断变墙的回合数的数组要初始化最大值

状态转移方程

dp[i][j] = max(dp[i-1][j],dp[i][j-1] + room[i][j])

#include <bits/stdc++.h>
using namespace std;
const int minn = -1e8;
int n,m,t,x,y,v,ans=0;
int dp[1002][1002]={0},room[1002][1002]={0},change[1001][1001]={0};

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,m;
	cin >> n >> m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin >> room[i][j];
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m;j++){
			dp[i][j] = -1e8;
			change[i][j] = 1e8;
		}
	}
	cin >> t;
	for(int i=0;i<t;i++){
		cin >> x >> y >> v;
		change[x][y] = v;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(i+j-2 >= change[i][j])
				room[i][j] = minn;
		}
	}
	ans = 0;
	dp[0][1] = 0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			dp[i][j] = max(dp[i-1][j],dp[i][j-1])+room[i][j];
			ans = max(ans,dp[i][j]);
		}
	}
	/*for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)
			cout << dp[i][j];
		cout << '\n';
	}*/
	cout << ans << '\n';
	return 0;
}

牛客周赛83

A B

签到

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	char inp;
	cin >> inp;
	if(inp == 'U' || inp == 'D')
		cout << "R\n";
	else cout << "U\n"; 
	return 0;
}

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		for(int i=1;i<=n;i++)
			cout << (i%2 ? 1:2) << ' ';
		cout << '\n';
	} 
	return 0;
}

C

构造,还是要点思维的,有坑。

构造一个“xx”形式的即可,如x=23,那么构造2323,2323 = 23*100+23,y就是101

坑点就在于pow函数那个地方要开long long,否则会爆

#include <bits/stdc++.h>
#define int long long
using namespace std;

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		string s;
		cin >> s;
		int si = s.size();
		cout << (long long)pow(10,si)+1 << '\n';
	}
	return 0;
}

E

DP

dp[i][j]表示移动到第i格花了j步

考虑来的方向:

dp[i-6][j-1] -> dp[i][j]

dp[i-5][j-1] -> dp[i][j]

dp[i-4][j-1] -> dp[i][j]

dp[i-3][j-1] -> dp[i][j]

dp[i-2][j-1] -> dp[i][j]

dp[i-1][j-1] -> dp[i][j]

因此遍历每个i j,注意从i-6这里可能会小于0,因此l = max(0ll,i-6)

状态转移方程为dp[i][j] = max(dp[i][j,dp[l][j-1]+a[i])

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int inf = -1e18;
const int sup = 1e4+1;
const int mid = 1e3+1;
const int maxn = 1e9+1;

int n,k;
vector<vector<int>> dp(sup,vector<int>(mid,inf));
int a[sup]; 
//dp[i][j]表示移动到第i格花了j步 

void solve(){
	cin >> n >> k;
	for(int i=1;i<=n;i++)
		cin >> a[i];
	int ans = inf;
	dp[0][0] = 0ll;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=min(i,k);j++)
			for(int l=max(0ll,i-6);l<=i-1;l++)
				dp[i][j] = max(dp[i][j],dp[l][j-1]+a[i]);
		ans = max(ans,dp[i][k]);
	}
	cout << ans << '\n';
	return;
}

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	solve();
	return 0;
}

牛客周赛84

A

题目

签到

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int a,b,c;
	cin >> a >> b >> c;
	if(a==b && b==c) cout << "Yes\n";
	else cout << "No\n"; 
	return 0;
}

B

题目

贪心,当数组是郑旭序列或倒序序列时,陡峭值是最小的。

数组元素相同时,正序倒序都一样,1个

数组元素不同是,正序倒序不一样,2个

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n;
	int a[101];
	cin >> n;
	for(int i=0;i<n;i++)
		cin >> a[i];
	sort(a,a+n);
	int pd=1,minn=0;
	for(int i=0;i<n-1;i++){
		minn += abs(a[i+1]-a[i]);
		if(a[i+1] != a[i]) pd=0;
	}
	cout << (pd ? 1:2) << ' ' << minn << '\n';
	return 0;
}

C

题目

由于n*k = 1e6,可以直接暴力模拟

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,k;
	cin >> n >> k;
	string s;
	cin >> s;
	int sum=0;
	for(int i=0;i+k<=n;i++){
		//cout << "i:"<<i<<'\n';
		for(int j=0;j<k-1;j++){
			//cout << "j:"<<j<<'\n';
			sum += abs(s[i+j+1]-s[i+j]);
			//cout << sum << '\n';
		}
	}
	cout << sum << '\n';
	return 0;
}

D

题目

n*k = 1e12,无法像上题一样暴力速解

想到了一个复杂度O(n)的办法

先做一个k=2的相邻元素之间的陡峭值数组,可以发现,随着k的变化,陡峭值数组的每个元素被加的次数呈现出对称性

且最多被加的次数可以算出来是int maxt = (k<=n/2+1 ? k-1:n-k+1)

因此走一遍数组,sum += vec[i]*times,即可

#include <bits/stdc++.h>
#define int long long
using namespace std;

vector <int> vec;

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,k;
	cin >> n >> k;
	string s;
	cin >> s;
	if(n==2){
		cout << abs(s[1]-s[0]) << '\n';
		return 0;
	}
	int maxt = (k<=n/2+1 ? k-1:n-k+1);
	//cout<<"maxt:"<<maxt<<'\n';
	for(int i=0;i+1<n;i++){
		vec.push_back(abs(s[i+1]-s[i]));
	}
	//cout<<"vec:\n";
	//for(auto i:vec) cout<<i<<' ';
	//cout << '\n';
	int si = n-1,sum = 0;
	int mid = si/2-1;
	int times = 1;
	//cout<<"si:"<<si<<" mid:"<<mid<<'\n';
	for(int i=0;i<=mid;i++){
		sum += vec[i]*times;
		sum += vec[si-i-1]*times;
		if(times < maxt) times++;
	}
	if(si%2) sum+=vec[si/2]*maxt;
	cout << sum << '\n';
	return 0;
}

周赛84的周日才打,先补到这,剩下的下周来

线段树

线段树的核心思想是分治,大区间的解可以由小区间的解合并而来,总复杂度为O(nlogn)。有两个基本应用场景:

  1. 区间最值问题。长为n的序列a,需要以下操作:
    1. 求区间[ i , j ]内的最值
    2. 修改a[k]为x
  2. 区间和问题。长为n的序列a,先更改某些数的值,再求区间[ i , j ]的和。

线段树建立在二叉树上,每次分治左右子树各一半,能利用二叉树的许多性质。

PS:对于任意一个节点 i ,其父亲节点是 i/2 , 其左孩子是 2i , 其右孩子是 2i+1 。

考察每个线段[L,R],L是左端,R是右端:

  1. L=R,说明这个节点只有一个元素,它是叶子节点。
  2. L<R,说明这个节点代表的不止一个点,那么它有两个孩子,左孩子区间为[L,M],右孩子区间为[M+1,R],其中M = (L+R)/2。

线段树的构造:

//定义根节点是tree[1],即编号为1的节点是根
int tree[N*4];			//用tree[i]记录线段i的最值或区间和
//p是父节点,ls(p)是左儿子,rs(p)是右儿子
int ls(int p){return p<<1;}		//左儿子,编号是p*2
int rs(int p){return p<<1|1;}	//右儿子,编号是p*2+1

void push_up(int p){						//从下向上传递区间值
    tree[p] = tree[ls(p)] + tree[rs(p)];	//区间和
  //tree[p] = min(tree[ls(p)],tree[rs(p)]); //最小值
}

void build(int p,int pl,int pr){			//节点编号p指向区间[pl,pr]
    if(pl == pr) {tree[p] = a[pl];return;}  //最底层的叶子节点,存叶子节点的值
    int mid = (pl+pr) >> 1;					//分治:折半
    build(ls(p),pl,mid);					//递归左儿子
    build(rs(p),mid+1,pr);					//递归右儿子
    push_up(p);								//从下往上传递区间值
}

区间查询:

int query(int L,int R,int p,int pl,int pr){
    if(L<=pl && R>=pr) return tree[p];				//完全覆盖
    int mid = (pl+pr) >> 1;
    if(L<=mid) 	 res += query(L,R,ls(p),pl,mid);	//L与左子节点有重叠
    if(R>=mid+1) res += queru(L,R,rs(p),mid+1,pr);	//R与右子节点有重叠
    return res;
}	//调用方式 query(L,R,1,1,n);

Layz-Tag:

线段树的节点tree[i]记录区间 i 的值,那么可以再定义一个tag[i],用它统一记录区间 i 的修改。每次修改的复杂度为O(log2n),一共m次操作,总复杂度O(mlog2n)

区间修改update()

先找到具体区间,对该节点即以上的节点做修改,并给该节点打tag。下次经过某节点且该节点tag不为0,那么就要把tag[p]传递给左右子树,并清空tag[p],这个过程用push_down()函数完成。

模板(洛谷 P3372)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
ll a[N];	//记录题目给出的数列的元素,从a[1]开始 
ll tree[N<<2];	//tree[i]为第i个节点的值,表示一个线段的值,如最值,区间和
ll tag[N<<2];	//tag[i]为第i个节点的Lazy-Tag,统一记录这个区间的修改
ll ls(ll p){return p<<1;}	//定位左儿子: p*2
ll rs(ll p){return p<<1|1;}	//定位右儿子: p*2+1
void push_up(ll p){		//从下向上传递区间值 
	tree[p] = tree[ls(p)] + tree[rs(p)]; //求区间和 
  //tree[p] = min(tree[ls(p)],tree[rs(p)]); 求最小值 
}
void build(ll p,ll pl, ll pr){//建树。p为节点编号,指向区间[pl,pr] 
	tag[p] = 0;  //Lazy-Tag标记 
	if(pl == pr){tree[p] = a[pl];return;}  //最底层的叶子,赋值 
	ll mid = (pl + pr) >> 1;  //分治,折半
	build(ls(p),pl,mid);	  //左儿子 
	build(rs(p),mid+1,pr);	  //右儿子
	push_up(p);				  //从下往上传递区间值 
}
void addtag(ll p,ll pl,ll pr,ll d){ //给节点ptag标记,并更新tree 
	tag[p] += d;			//打上tag标记 
	tree[p] += d*(pr-pl+1); //计算新的tree 
}
void push_down(ll p,ll pl,ll pr){  //不能覆盖时,把tag传给子树 
	if(tag[p]){		//有tag标记,以前修改留下的 
		ll mid = (pl+pr)>>1;
		addtag(ls(p),pl,mid,tag[p]);	//tag传给左子树 
		addtag(rs(p),mid+1,pr,tag[p]);	//tag传给右子树 
		tag[p] = 0;		//自己的tag清零 
	}
}
void update(ll L,ll R,ll p, ll pl,ll pr,ll d){ //区间修改,每个元素加d 
	if(L<=pl && pr<=R){		//完全覆盖,直接返回这个节点,它的子树不用继续再深入
		addtag(p,pl,pr,d);  //给p节点打标记,下一次修改会用到 
		return;
	}
	push_down(p,pl,pr);	 //如果不能覆盖,就把tag传给子树
	ll mid = (pl+pr)>>1;
	if(L <= mid) update(L,R,ls(p),pl,mid,d);	//递归左子树
	if(R > mid) update(L,R,rs(p),mid+1,pr,d);	//递归右子树
	push_up(p);	//更新 
}
ll query(ll L,ll R,ll p,ll pl,ll pr){
	//查询区间[L,R],P是当前节点(线段)的编号,[pl,pr]是节点p表示的线段区间
	if(pl >= L&&R >= pr) return tree[p];	//完全覆盖,直接返回
	push_down(p,pl,pr);		//不能覆盖,递归子树
	ll res = 0;
	ll mid = (pl+pr)>>1;
	if(L<=mid) res+=query(L,R,ls(p),pl,mid);  //左子节点有重叠
	if(R>mid) res+=query(L,R,rs(p),mid+1,pr); //右子节点有重叠
	return res; 
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	ll n,m;
	cin >> n >> m;
	for(ll i=1;i<=n;i++)
		cin >> a[i];
	build(1,1,n);		//建树 
	while(m--){
		ll q,L,R,d;
		cin >> q;
		if(q == 1){		//区间修改,每个元素加d 
			cin >> L >> R >> d;
			update(L,R,1,1,n,d);
		}
		else{			//区间查询,[L,R]区间和 
			cin >> L >> R;
			cout << query(L,R,1,1,n) << '\n';
		}
	}
	return 0;
}
posted @ 2025-06-14 12:23  HLAIA  阅读(9)  评论(0)    收藏  举报