巅峰赛#22 全题解
2025-06-22 23:05:24
发布于:广东
个人难度:红橙橙黄绿绿
T1:模拟题 个人难度红
对于每一个 中的字符串,暴力检查是否和 中的字符串相同并打标记。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 10005;
int n,m,a[N];
bool tag[N];
string s[N],t[N];
map<string,int> ma;
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s[i];
}
for(int i=1;i<=m;i++){
cin>>t[i];
for(int j=1;j<=n;j++){
if(t[i]==s[j]){
tag[j] = 1;
}
}
}
for(int i=1;i<=n;i++){
if(!tag[i]) cout<<s[i]<<endl;
}
return 0;
}
T2:思维题,个人难度橙。
首先分析题意:选出一个子序列,使得它们的gcd+1,求最终整个序列gcd的最大值。看到数据范围很容易想到枚举选出的子序列的gcd。接着考虑:含有这个枚举数作为因子的数必须全部选上,因为这必定是不劣的。再考虑剩下所有数的gcd,最终整个数列的gcd相当于对这两个数再取一次gcd,记录最大值即可。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 10005;
int n,m,a[N];
inline int gcd(int x,int y){
if(y==0) return x;
return gcd(y,x%y);
}
signed main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int d1=0,d2=0;
int ans = 1;
for(int d=1;d<=5000;d++){
d1 = d2 = 0;
for(int i=1;i<=n;i++){
if(a[i]%d==0){
if(!d1) d1 = a[i];
else d1 = gcd(d1,a[i]);
}
else{
if(!d2) d2 = a[i];
else d2 = gcd(d2,a[i]);
}
}
if(d2%(d1+1)==0){
ans = max(ans,d1+1);
}
}
cout<<ans;
return 0;
}
T3:模拟题。个人难度橙。
常规思路是先考虑个位,统计所有可能的进位,如果进位的数不能直接凑出来就再考虑十位填数,以此类推...最多填8位。
但是我们手玩一下会发现如果有解,那么最短的解必定不超过2位数,相加的结果也就不会超过3位数。因此我们直接枚举 ,一共不超过 种情况,可以通过。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 10005;
int n,m;
char ta[N],tb[N],tc[N];
int a[N],b[N],c[N];
int la,lb,lc;
inline void dfs(int dth,int n1,int n2){
if(dth==1){
for(int i=0;i<=la;i++){
for(int j=1;j<=la;j++){
dfs(2,a[i]*10+a[j],n2);
}
}
}
else if(dth==2){
for(int i=0;i<=lb;i++){
for(int j=1;j<=lb;j++){
dfs(3,n1,b[i]*10+b[j]);
}
}
}
else{
for(int i=0;i<=lc;i++){
for(int j=0;j<=lc;j++){
for(int k=1;k<=lc;k++){
if(c[i]*100+c[j]*10+c[k]==n1+n2){
cout<<n1<<' '<<n2<<' '<<n1+n2;
exit(0);
}
}
}
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin>>ta>>tb>>tc;
la = strlen(ta),lb = strlen(tb),lc = strlen(tc);
for(int i=0;i<la;i++){
a[i+1] = ta[i]-'0';
}
for(int i=0;i<lb;i++){
b[i+1] = tb[i]-'0';
}
for(int i=0;i<lc;i++){
c[i+1] = tc[i]-'0';
}
dfs(1,0,0);
cout<<"impossible";
return 0;
}
T4:dp题,个人难度黄。
首先很容易想到这是一个完全背包,但是价值的条件不一样。容易想到先钦定一个物品为幸福度较小的那个,然后贪心地找价格最小的与它配对。方法是按幸福度从大到小排序,记录当前扫到的物品的最小价格。将输入的价值加上配对物的价值作为最终价值跑完全背包即可。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 10005;
int n,m,f[N];
struct node{
int cost,val;
}no[N];
bool cmp(node x,node y){
if(x.val==y.val) return x.cost>y.cost;
return x.val>y.val;
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>no[i].cost>>no[i].val;
}
sort(no+1,no+n+1,cmp);
int maxn = 0x3f3f3f3f;
for(int i=1;i<=n;i++){
int tmp = maxn;
maxn = min(maxn,no[i].cost);
no[i].cost += tmp;
}
for(int i=1;i<=n;i++) for(int j=no[i].cost;j<=m;j++) f[j] = max(f[j],f[j-no[i].cost]+no[i].val);
int ans = 0;
for(int i=1;i<=m;i++){
ans = max(ans,f[i]);
}
cout<<ans;
return 0;
}
T5:数据结构题,个人难度绿。
这题不少人可能会用线段树等做法做,但是我赛时没往这方向想,而是想到了值域树状数组+扫描线做法。
首先考虑:能与询问区间相交的区间分为3类:左端点在询问区间左边的,右端点在询问区间右边的,被询问区间完全包含的。(注意这三类可能有重复,但是这不重要,因为我们求的是最大值)
对于 两种情况,我们可以用前缀最大值 表示左端点在 内的区间的右端点最大值,后缀最小值 同理。很容易想到以此计算出这两种情况的方法。对于第三种情况,我们把询问区间和给定区间按左端点从大到小排序,对于每一个扫到的区间左端点,在它的右端点处向树状数组添加它的长度(树状数组为取最大值的树状数组)。统计每个询问区间时查询以询问区间右端点为边界的树状数组最大值即可。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define lowbit(x) x & (-x)
using namespace std;
const int N = 5e5+10;
int n,m,c[N],q;
int tmpx[N],tmpy[N];
int pre[N],lmax[N],rmin[N],res[N];
inline void add(int x,int v){
while(x<=n){
c[x] = max(c[x],v);
x += lowbit(x);
}
}
inline int get(int x){
int res = 0;
while(x){
res = max(res,c[x]);
x -= lowbit(x);
}
return res;
}
struct node{
int l,r;
}no[N];
struct edge{
int l,r,ans,lim,id;
}qu[N];
inline bool cmp(node x,node y){
return x.l>y.l;
}
inline bool cmp2(edge x,edge y){
return x.l>y.l;
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m>>q;
for(int i=1;i<=n;i++) tmpy[i] = 1e18;
for(int i=1;i<=m;i++){
cin>>no[i].l>>no[i].r;
if(tmpx[no[i].l]<no[i].r) tmpx[no[i].l] = no[i].r;
if(tmpy[no[i].r]>no[i].l) tmpy[no[i].r] = no[i].l;
}
rmin[n+1] = 1e18;
for(int i=1;i<=n;i++){
lmax[i] = max(lmax[i-1],tmpx[i]);
}
for(int i=n;i>=1;i--){
rmin[i] = min(rmin[i+1],tmpy[i]);
}
int q1,q2,q3;
for(int i=1;i<=q;i++){
cin>>q1>>q2>>q3;
int tmp = lmax[q1]-q1+1,tmp2 = q2-rmin[q2]+1;
int ans = min(max(tmp,tmp2),q2-q1+1);
qu[i] = {q1,q2,ans,q3,i};
}
sort(no+1,no+m+1,cmp);
sort(qu+1,qu+q+1,cmp2);
int p = 1;
for(int i=1;i<=q;i++){
while(p<=m && no[p].l>=qu[i].l){
add(no[p].r,no[p].r-no[p].l+1);
++p;
}
qu[i].ans = max(qu[i].ans,get(qu[i].r));
}
for(int i=1;i<=q;i++){
if(qu[i].ans>=qu[i].lim) res[qu[i].id] = 1;
}
for(int i=1;i<=q;i++){
if(res[i]==1) cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}
T6:计数题,个人难度绿。
首先考虑根节点怎么计数:使用树形dp, 表示以 为根的子树的bfs序种类。首先预处理出每个节点子树大小 ,接着我们发现我们每一步可以向任何一个子节点的子树前进一步,且bfs序长度恰为 。于是对于每个子树,我们在 个位置里选择 个位置作为它子树bfs序的位置,再乘以内部排列顺序 ,即:
。
接着考虑剩下的节点:对于某个节点 ,我们可以在它原本的子树上扩展或是向它的父节点扩展。假设我们已经计算出父节点的最终答案 ,那么向父节点扩展的方案数恰是 除去 所在子树的情况。同样分析思路得:
直接计算即可。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int M = 998244353;
const int N = 1e6+10;
int n,m,a[N];
int pre[N],inv[N],siz[N],f[N],ans[N];
vector<int> vec[N];
inline int c(int x,int y){return pre[x]*inv[y]%M*inv[x-y]%M;}
inline void ExPower( int b, int p, int & a, int & k ){
if( p == 0 ) {
a = 1; k = 0;
return;
}
ExPower( p, b % p, k, a );
k -= b / p * a;
return;
}
inline int ny(int x){
int t1,t2;
ExPower(x,M,t1,t2);
if(t1<0) t1+=M;
return t1;
}
void dfs(int x,int fa){
siz[x] = 1;
for(int v:vec[x]){
if(v==fa) continue;
dfs(v,x);
siz[x] += siz[v];
}
}
void dfs2(int x,int fa){
f[x] = 1;
int rest = siz[x]-1;
for(int v:vec[x]){
if(v==fa) continue;
dfs2(v,x);
f[x] = (f[x]*c(rest,siz[v])%M*f[v])%M;
rest -= siz[v];
}
if(x==1) ans[1] = f[1];
}
void dfs3(int x,int fa){
if(x>1){
ans[x] = ans[fa]*siz[x]%M*ny(n-siz[x])%M;
}
for(int v:vec[x]){
if(v==fa) continue;
dfs3(v,x);
}
}
signed main(){
ios::sync_with_stdio(false);
pre[0] = 1,inv[0] = inv[1] = 1;
for(int i=2;i<=1e6;i++) inv[i] = (M-M/i)*inv[M%i]%M;
for(int i=2;i<=1e6;i++) inv[i] = (inv[i-1]*inv[i])%M;
for(int i=1;i<=1e6;i++) pre[i] = (pre[i-1]*i)%M;
cin>>n;
int q1,q2;
for(int i=1;i<=n-1;i++){
cin>>q1>>q2;
vec[q1].push_back(q2);
vec[q2].push_back(q1);
}
dfs(1,0);
dfs2(1,0);
dfs3(1,0);
for(int i=1;i<=n;i++){
cout<<ans[i]<<endl;
}
return 0;
}
全部评论 3
还是扫描线大佬
15小时前 来自 北京
0%%%%%%%%%%%%%%%%
15小时前 来自 北京
0手机上发的,很多细节可能略有简略,有疑问的欢迎发在评论区
22小时前 来自 广东
0
有帮助,赞一个