2.24~3.2

牛客周赛81

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 if(a+1==b && b+1==c) cout << "Yes\n";
	else cout << "NO\n";
	return 0;
}

B

题目

一颗完全二叉树,如果每个节点的两个子节点都不小于它,那么这称颗二叉树是“平衡的”。数据量不大,把树储存在二维数组里,逐节点判断即可

如果某个节点满足G[i][j] > G[i+1][j] || G[i][j] > G[i+1][j+1]则不行

代码

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

int G[1005][1005];

void solve(int* n){
	for(int i=1;i<*n;i++)
		for(int j=1;j<=i;j++)
			if(G[i][j] > G[i+1][j] || G[i][j] > G[i+1][j+1]){
				cout << "No\n";
				return;
			}
	cout << "Yes\n";
	return;
}

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t,n;
	cin >> t;
	while(t--){
		cin >> n;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=i;j++)
				cin >> G[i][j];
		solve(&n);
	}
	return 0;
}

C

题目

看到本题的数据量,就知道不能用B的方法模拟树来找,会爆栈。

本题应用二分找到 l 和 r 的层数,如果 l 和 r 在同一层,那么颠倒后一样平衡。容易想不到的是 l 和 r 在相邻层,且 l + i(层数) > r 时同样平衡

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e9;
int t,n,li,ri;

int erfen(int li){
	int l=1,r=(1<<30),mid,x2 = 2*li;
	while(l<r){
		mid = (l+r)>>1;
		int x1 = mid*(mid+1);
		if(x1 < x2)
			l = mid+1;
		else if(x1 > x2)
			r = mid;
		else return mid;
	}
	return l;
}

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> t;
	while(t--){
		cin >> n >> li >> ri;
		int c1 = erfen(li), c2 = erfen(ri);
		if(c1 == c2) cout << "Yes\n";
		else if(c1+1 == c2 && li+c1 > ri) cout << "Yes\n";
		else cout << "No\n";
	}
	return 0;
}

D

题目

对于 构造 这一类题,很多都是贪心

本体也贪,要使得塔平衡,就要使得每一层数字在满足条件的情况下尽可能小

模拟这颗树,每一层与上一层的数相比,用lower_bound()好使

代码

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

void solve(){
	int n;
	cin >> n;
	vector <vector<int>> G(n,vector<int>(n));
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)
			cin >> G[i][j];
		sort(G[i].begin(),G[i].end());
	}
	int pos = 0;
	vector <int>::iterator it;
	vector <int> vec1,vec2 = {G[0][0]};
	for(int i=1;i<n;i++){
		for(int j=0;j<i;j++){
			it = lower_bound(G[i].begin()+pos,G[i].end(),vec2[j]);
			if(it == G[i].end()){
				cout << i << '\n';
				return;
			}
			vec1.push_back(*it);
			pos = it-G[i].begin()+1;
		}
		if(n > pos-1)
			vec1.push_back(G[i][pos]);
		else{
			cout << i << '\n';
			return;
		}
		vec2 = vec1;
	}
	cout << n << '\n';
	return;
}

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		solve();
	}
	return 0;
}

牛客周赛82

A

题目

代码

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

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	string s;
	cin >> s;
	if(s[0] == s[2]) 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,cache;
	cin >> n;
	set <int> s;
	for(int i=0;i<n;i++){
		cin >> cache;
		s.insert(cache);
	}
	if(s.size() == n) cout << "YES\n";
	else cout << "NO\n";
	return 0;
}

题目如图

观察发现,拍不了照的情况就是:你在本窗口拍照时,还有别的窗口的队伍是空的。

所以,能拍照的条件就是,每一条队伍的人数都互不相同

C

题目

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+1;

pair<int,int> a[maxn];
map<int,int> ma;
bool cpa(pair<int,int> a,pair<int,int> b){
	return a.first < b.first;
}

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,cache,pd=1;
	cin >> n;
	for(int i=0;i<n;i++){
		cin >> cache;
		a[i] = {cache,i+1};
		if(!ma[cache]) ma[cache]++;
		else pd=0;
	}
	if(pd){
		sort(a,a+n,cpa);
		cout << "YES\n";
		for(int i=0;i<n;i++)
			cout << a[i].second << ' ';
	}
	else cout << "NO\n";
	return 0;
}

与B题差不多

先将队伍人数从小到大排序,然后输出每个队伍的ID即可

D

题目

题目如图

由于排列是递减的,所以一旦有第i项大于第i-1项的情况,为0

对输入的排列进行逐项判断,后一项与前一项不同,更新ave

后一项与前一项相同,ans *= ave,ave--

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int sup = 1e9+1;
const int maxn = 2e5+1;
const int mod = 998244353;
int prefix[maxn],t,n;

void solve(){
	int ans=1, ave=n-prefix[1];
	for(int i=2;i<=n;i++){
		if(prefix[i] == prefix[i-1]){
			ans *= ave;
			ave--;
		}
		else if(prefix[i] > prefix[i-1]){
			cout << "0\n";
			return;
		}
		else ave += prefix[i-1] - prefix[i] -1;
	}
	ans %= mod;
	cout << ans << '\n';
}

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> t;
	prefix[0] = sup;
	while(t--){
		cin >> n;
		for(int i=1;i<=n;i++)
			cin >> prefix[i];
		solve();
	}
	return 0;
}

洛谷FAOI-R4

A

题目

签到,字符串处理,如果输入的字符串的首字母为数字,那么输出时就加上P,否则直接输出

代码

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

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	string wz = "https://www.luogu.com.cn/problem/";
	string k = "PBCASUT";
	int t;
	cin >> t;
	while(t--){
		string inp;
		cin >> inp;
		int pd = 1;
		for(int i=0;i<7;i++)
			if(inp[0] == k[i]){
				cout << wz << inp << '\n';
				pd = 0;
				break;
			}
		if(pd) cout << wz << 'P' << inp << '\n';
	}
	return 0;
}

B

题目

题目如图

限制条件有点多,很容易看漏/想漏...qwq...

先把m拆为二进制数的形式,再判断m的长度是否为二次幂,对于这个判断,我先做个预处理打表,判断时查表。如果m长度不是二次幂,那么从最小位开始,把m拆开,遇到1就跳过,直到长度为二次幂为止,这个过程用deque实现

代码

#include <bits/stdc++.h>
#define int long long 
using namespace std;
const int maxn = 2e6+1;

vector <int> er;  
deque <int> q;
bool biao[maxn];

void yu(){
	int sup = maxn/2;
	for(int i=1;i<=sup;i*=2)
		biao[i] = true;
}

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	yu();
	int t;
	cin >> t;
	while(t--){
		int m;
		cin >> m;
		while(m>0){
			int yu = m%2;
			er.push_back(yu);
			m/=2;
		}
		int si = er.size(),pd=0;
		for(int i=0;i<si;i++)
			if(er[i])
				q.push_back(pow(2,i));
		si = q.size();
		while(!biao[si]){
			int temp = q.front();
			q.pop_front();
			if(temp == 1){
				cout << 1 << ' ';
				continue;
			}
			temp/=2;
			si++;
			q.push_front(temp);
			q.push_front(temp);
		}
		for(auto it:q)
			cout << it << ' ';
		cout << '\n';
		er.clear();
		q.clear();
	}
	return 0;
}

树状数组

可以高效率地查询和维护前缀和(或区间和);当序列是动态变化的时,如果改变其中一个元素ai的值,那么后面的前缀和都要重新计算,查询一次的复杂度就会变成O(n)。但是树状数组可以用简短的代码大大提高效率,查询和维护的复杂度都为O(log2n)

lowbit(x)

找x的二进制数的最后一个1对应的十进制是多少。

lowbit(x) = x & -x

例如,lowbit(5) = 5 & -5 = 1

lowbit(6) = 6 & -6 = 2lowbit(8) = 8 & -8 = 8

编码

#incldue <bits/stdc++.h>
using namespace std;
const int N = 1000;
#define lowbit(x) ((x) & -(x))
int tree[N] = {0};

void update(int x,int d){	//单点修改:修改元素a[x], a[x] = a[x] + d;
    while(x <= N){
        tree[x] += d;
        x += lowbit(x);
    }
}

int sum(int x){		//查询前缀和:返回前缀和sum[x] = a[1] + a[2] +...+ a[x]
    int ans = 0;
    while(x > 0){
        ans += tree[x];
        x -= lowbit(x);
    }
    return ans;
}
//以上是树状数组相关
int a[11] = {0,4,5,6,7,8,9,10,11,12,13}		//注意a[0]拿来占位的,不用

int main(){
    for(int i=1;i<=10;i++)
        update(i,a[i]);		//初始化tree[]数组
    cout << "old:[5,8] = "<< sum(8) - sum(4) << '\n'; //查询区间和[5,8] = 38
    update(5,100);		//修改a[5]增加100
    cout << "new:[5,8] = "<< sum(8) - sum(4) << '\n'; //查询新和 = 138
    return 0;
}

以HDU 1556 为例

题目

总所周知,差分是前缀和的逆运算,求某点维护后的值,相当于求差分的前缀和。

树状数组正好适合来处理前缀和。

这里用树状数组tree[]来模拟差分,最后的sum(i)就相当于输出a[i]

代码

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
using namespace std;
const int maxn = 100002;
int N,tree[maxn];

void update(int x,int d){
	while(x <= N){
		tree[x] += d;
		x += lowbit(x);
	}
}

int sum(int x){
	int ans = 0;
	while(x > 0){
		ans += tree[x];
		x -= lowbit(x);
	}
	return ans;
}

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	while(cin >> N){
		memset(tree,0,sizeof(tree));
		int a,b;
		for(int i=0;i<N;i++){
			cin >> a >> b;
			update(a,1);
			update(b+1,-1);
		}
		for(int i=1;i<=N;i++)
			cout << sum(i) << ' ';
		cout << '\n';
	}
	return 0;
}
posted @ 2025-06-14 12:23  HLAIA  阅读(11)  评论(0)    收藏  举报