牛客 周赛82 20250227

牛客 周赛82 20250227

https://ac.nowcoder.com/acm/contest/102303

A:
题目大意:给定字符串 \(s\) ,判断首尾是否相同

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;


int main()
{
	string s;
	cin>>s;
	if(s[0]==s[s.size()-1]) cout<<"YES";
	else cout<<"NO";	
	return 0;
}

简单签到

B:

题目大意:

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;

int n;
int a[1010];

int main()
{
	cin>>n;
	for (int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n);
	for (int i=2;i<=n;i++){
		if (a[i]==a[i-1]){
			cout<<"NO";
			return 0;
		}
	}
	cout<<"YES";
	return 0;
}

将窗口人数从小到大排序,当且仅当 \(a_{i+1}>a_i\) 时才能保证有充足的时间进行拍照操作

C:

题目大意:与B题共享背景,数据范围增大且需要输出排队路径

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;

struct node{
	int t,pos;
};

int n;
vector<node> a;

int main()
{
	cin>>n;
	for (int i=1;i<=n;i++){
		int x;
		cin>>x;
		a.push_back({x,i});
	}
	sort(a.begin(),a.end(),[](node x,node y){return x.t<y.t;});
	for (int i=1;i<n;i++){
		if (a[i].t==a[i-1].t){
			cout<<"NO";
			return 0;
		}
	}
	cout<<"YES"<<endl;
	for (auto iter:a)
		cout<<iter.pos<<' ';
	return 0;
}

将每个窗口的编号与人数绑定,按照人数排序后,与B题类似的进行操作,最后输出窗口编号即可

D:
题目大意:有一个 \([1,n]\) 的排列,现在给出确定前缀最小值数组 \(a\) ,计算满足这个前缀最小值数组的排列个数

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;

const int mod=998244353;

void solve(void){
	int n;
	cin>>n;
	vector<int> a(n+10);
	for (int i=1;i<=n;i++) cin>>a[i];
	a[0]=1e9;
	LL ans=1;
	int now=n-a[1];
	for (int i=2;i<=n;i++){
		if (a[i]>a[i-1]){
			cout<<0<<endl;
			return;
		}
		if (a[i]==a[i-1]){
			ans=ans*now%mod;
			now--;
		}
		if (a[i]<a[i-1]){
			now+=a[i-1]-a[i]-1;
		}
		ans%=mod;
	}
	cout<<ans<<endl;
}

int main()
{
	Trd;
	
	return 0;
}

前缀最小值数组的性质存在: \(a_{i}<a_{i-1}\implies\) 可以确定填在 \(i\) 位置上的数为 \(a_i\)

所以根据 \(a_i\) ,考虑每个位置上的数能选取的数字个数

初始化时,now=n-a[1] ,即从第二位开始考虑,能选的数字个数

  • a[i]>a[i-1] 显然不能构成一个正确的排列,输出 \(0\)

  • a[i]==a[i-1] 当前位置上没有确定数字,那么从可选的数字中取一个填入,对答案的贡献根据乘法原理,ans=ans*now

  • a[i]<a[i-1] 当前位置可以确定一个数,更新可选的数 now+=a[i-1]-a[i]-1

巧妙的是,过程中如果某一位无数可选那么对应的 \(now=0\) ,所以 \(ans\times0=0\) ,最后输出答案同样为 \(0\) ,符合题意

E:

题目大意:给定 \(n\) 个元素的两个数组 \(a,b\) ,从小到大选取 \(2\times m\le n\) 个下标,计算对于所有的下标组合得出的表达式的最小值

\[a_{i_1}+a_{i_2}+\cdots+a_{i_m}+b_{i_{m+1}}+b_{i_{m+2}}+\cdots+b_{i_{2*m}} \]

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;

int main()
{
    int n,m;
	cin>>n>>m;
    
	priority_queue<int> qa;
	LL suma=0;
	vector<LL> pa(n+10);
	for (int i=1;i<=n;i++){
		int a;
		cin>>a;
		if(i<=m){
			qa.push(a);
			suma+=1ll*a;
		}else{
			if (a<qa.top()){
				suma-=qa.top();
				qa.pop();
				qa.push(a);
				suma+=a;
			}
		}
		if (i>=m)
			pa[i]=suma;
	}
	
	vector<int> b(n+10);
	for (int i=1;i<=n;i++) cin>>b[i];
	
	priority_queue<int> qb;
	LL sumb=0;
	vector<LL> pb(n+10);
	for (int i=n;i>=1;i--){
		if (i>=n-m+1){
			qb.push(b[i]);
			sumb+=1ll*b[i];
		}else{
			if (b[i]<qb.top()){
				sumb-=qb.top();
				qb.pop();
				qb.push(b[i]);
				sumb+=b[i];
			}
		}
		if (i<=n-m+1)
			pb[i]=sumb;
	}
	
	LL ans=LLinf;
	for (int i=m;i<=n-m;i++)
		ans=min(ans,pa[i]+pb[i+1]);
	cout<<ans;
	return 0;
}

预处理+堆优化

因为 $2\times m\le n\iff m\le \frac{n}{2} $,所以需要选取的 \(a_{i_1}+a_{i_2}+\cdots+a_{i_m}+b_{i_{m+1}}+b_{i_{m+2}}+\cdots+b_{i_{2*m}}\) 可以看作在 \(a\) 中选 \(m\) 个元素,在 \(b\) 中从 \(m+1\) 的下标开始再选 \(m\) 个元素

可以这样对称地来看

\[\overbrace{a_1,a_2,\cdots,a_i}^{{\rm{count}}\ m},\cdots a_{n-1},a_n \\ b_1,b_2,\cdots,\overbrace{b_{i+1},\cdots b_{n-1},b_n}^{{\rm{count}}\ m} \]

于是可以预处理出 \(a\) 数组前 \(i\) 个元素选出 \(m\) 个元素的最小和,以及 \(b\) 数组后 \(i\) 个元素选出 \(m\) 个元素的最小和

采用优先队列大根堆优化,注意边界情况

if(i<=m){
	qa.push(a);
	suma+=1ll*a;
}//在a中选,只有i>m后才有意义

if (i>=m)
	pa[i]=suma;
//边界情况,i=m时也需要记录

最后的答案

for (int i=m;i<=n-m;i++)
		ans=min(ans,pa[i]+pb[i+1]);

枚举 \(i_m\) 分界线,范围在 \([m,n-m]\) 内,记录所需最小值即可

F:

题目大意:给定 \(n\) ,存在数组满足数组内每一个元素都是 \([1,n]\) 之间的整数,并且数组的每一个非空连续子区间都至少存在一个 "唯一元素" ,现构造一个由 \(n\) 个元素组成的、"种类数" 最少的这类数组

*唯一元素:定义一个非空数组的 "唯一元素" 为该数组中只出现一次的元素。一个数组可能有多个 "唯一元素" 、也可能没有 "唯一元素"

*种类数:定义一个数组的 "种类数" 为该数组中不同的元素个数

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;


int main()
{
	int n;
	cin>>n;
	vector<int> a;
	a.push_back(1);
	int cnt=1;
	while (a.size()<n){
		vector<int> b;
		for (auto it:a) b.push_back(it);
		bool f=0;//标记是否到n
		for (int i=0;i<b.size()-1;i++){
			if (a.size()==n){
				f=1;
				break;
			}
			a.push_back(b[i]);//类似倍增的构造
		}
		if (f) break;
		a.push_back(++cnt);//修改最后的字符
	}
	cout<<cnt<<endl;
	for (auto it:a) cout<<it<<' ';
	return 0;
}

思维构造

构造数组,当其中一个连续数组跨度为偶数时,其中的元素一定存在惟一个

将这个连续数组抽象为 \(A+B\) ,那么 \(A,B\) 一定不相同,贪心地想需要种类数最少情况,所以只让 \(B\) 其中一个元素与 \(A\) 不同即可

例如

1
1 2
1 2 1 3
1 2 1 3 1 2 1 4
1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 5 

\(B\) 除开最后一个字符与 \(A\) 不同,其他元素都可以由 \(A\) 复制过来,这样构造出的数组种类数一定最少,并且可以由前面的状态递推出

类似于倍增地计算,当 \(n\)\(2\) 的整数幂时,\(B\) 最后的字符需要修正为一个新元素

数据量是10^3,手动打表只需要10次

posted @ 2025-03-02 16:59  才瓯  阅读(27)  评论(0)    收藏  举报