第七届集美大学程序设计竞赛(个人赛)题解

2023-10-19 01:59

本文主要是介绍第七届集美大学程序设计竞赛(个人赛)题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考难度:
签到题:B、D
⭐:C
⭐⭐:H、J
⭐⭐⭐:I、G
⭐⭐⭐⭐:A、E、F

第七届集美大学程序设计竞赛(个人赛)题解

  • A. 送分题
    • 思路
    • 代码
  • B. 数列问题
    • 题意
    • 解题思路
    • 代码
  • C. 勋总的英语试卷
    • 题意
    • 思路
    • 代码
  • D. 签到题
    • 题意
    • 解题思路
    • 代码
  • E. csgo
    • 解题思路
    • 代码
  • F. Total order
    • 解题思路
    • 代码
  • G. 史上最强控分选手LJL
    • 题意
    • 思路
    • 代码
  • H. 114514
    • 题意
    • 解题思路
    • 代码
  • I. 大佬的字符串
    • 题意
    • 思路
    • 代码
  • J. 特殊的合并
    • 解题思路
    • 代码

A. 送分题

思路

要算的式子可以变成在这里插入图片描述
很明显按 x i x_i xi大小顺序排序会很方便。因为要范围查询还有单点修改,所以用线段树或者平衡树。
假如用线段树的话,树的每个点代表区间 [ l , r ] [l,r] [l,r]范围的答案,记录三个值,(数量,总和,答案)
叶子时, l = r l=r l=r,所以该点的值为在这里插入图片描述

结合两个区间时 ,右区间的 l ≥ l≥ l左区间的r,生成的点的值为(左区间数量+右区间数量,左区间总和+右区间总和 ,左区间答案+右区间答案+右区间总和左区间大小-左区间总和右区间大小) ,因为右区间内的任何一个 x j x_j xj都比左区间内的 x i x_i xi任何一个大 , 所以要 − - 右区间数量 ∗ * 左区间总和,而左区间内部的 x i x_i xi x j x_j xj产生的答案已经在左区间答案记录了,不需要再算;右区间同理,只是要 + + +
平衡树时,因为每个点都代表一个 x i x_i xi ,所以根也要算上,每个子树当作一个区间。
方法有点像在排序后的数组上点分治。如果只看一个 x i x_i xi,它跟排序后左边的区间结合,会 − - 左区间数量 ∗ x i *x_i xi,跟右边的区间结合,会右区间数量* ,直到扩展到要求的区间。
原题链接:https://codeforces.com/contest/295/problem/E

代码

参考答案:
动态线段树(作者:Arpa ,出题人微改):

//God & me
#include <bits/stdc++.h>
#define pb push_back
#define X first
#define Y second
using namespace std;
template<class T,class L>bool smax(T&x,L y){return x<y?(x=y,1):0;}template<class T,class L>bool smin(T&x,L y){return x>y?(x=y,1):0;}
typedef pair<int,int> pii;const int maxn=1e5+17;
struct node{long long sum,ans;int cnt,s,e;node * l,* r;node (int c=0,long long ss=0,long long a=0,int st=-1e9,int en=1e9+1){cnt=c,sum=ss,ans=a,s=st,e=en,l=r=NULL;}node mrg(const node &od){return node(od.cnt+cnt, od.sum+sum , od.ans + ans + od.sum * cnt - sum * od.cnt);}void plant(){int mid=(s+e)/2;if(l==NULL)l=new node(0,0,0,s,mid);if(r==NULL)r=new node(0,0,0,mid,e);}node smoke(int st,int en){if(st<=s && e<=en)return *this;if(en<=s || e<=st)return node();plant();return (l -> smoke(st,en)).mrg(r -> smoke(st,en));}void water(int p,int nut=1){if(s+1==e){cnt+=nut, ans=0,sum=cnt*p;return ;}int mid=(s+e)/2;plant();if(p<mid)l -> water(p,nut);elser -> water(p,nut);node tmp=l -> mrg(*r);cnt=tmp.cnt , ans = tmp.ans , sum = tmp.sum;}
}*root;
int n,p[maxn];
int main(){ios::sync_with_stdio(0),cin.tie(0);root = new node();cin>>n;for(int i=0;i<n;i++)cin>>p[i],root->water(p[i]);int q;cin>>q;for(int t,x,y;q--;){cin>>t>>x>>y;if(t==1)root->water(p[--x],-1),root->water(p[x]+=y);elsecout<<root->smoke(x,y+1).ans<<endl;}return 0;
}
离散化线段树(作者:Keshi)//In the name of GOD
#include <bits/stdc++.h>
using namespace std;typedef long long ll;const ll maxn = 2e6 + 100;
const ll mod = 1e9 + 7;
const ll inf = 1e18;#define fast_io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define file_io freopen("input.txt" , "r+" , stdin) ; freopen("output.txt" , "w+" , stdout);
#define pb push_back
#define Mp make_pair
#define pll pair<ll, ll>
#define F first
#define S secondstruct dat{ll cnt, sum, dis;
};ll n, m, k, a[maxn], b[maxn];
dat seg[maxn];
vector<ll> p;
vector<pair<ll, pll> > q;
vector<dat> vec;dat mrg(dat p1, dat p2){dat p3;p3.cnt = p1.cnt + p2.cnt;p3.sum = p1.sum + p2.sum;p3.dis = p1.dis + p2.dis + p1.cnt * p2.sum - p1.sum * p2.cnt;return p3;
}void upd(ll id, ll s, ll e, ll l, ll x, ll y){if(l < s || e <= l) return;if(e - s == 1){seg[id].cnt += x;seg[id].sum += y;return;}ll mid = (s + e) / 2;upd(id * 2, s, mid, l, x, y);upd(id * 2 + 1, mid, e, l, x, y);seg[id] = mrg(seg[id * 2], seg[id * 2 + 1]);return;
}
void get(ll id, ll s, ll e, ll l, ll r){if(r <= s || e <= l) return;if(l <= s && e <= r){vec.pb(seg[id]);return;}ll mid = (s + e) / 2;get(id * 2, s, mid, l, r);get(id * 2 + 1, mid, e, l, r);return;
}
ll solve(ll l, ll r){vec.clear();get(1, 0, k, l, r);for(ll i = 1; i < vec.size(); i++){vec[i] = mrg(vec[i - 1], vec[i]);}if(vec.empty()) return 0;return vec[vec.size() - 1].dis;
}int main(){fast_io;ll t, l, r;cin >> n;for(ll i = 0; i < n; i++){cin >> a[i];b[i] = a[i];p.pb(b[i]);}cin >> m;for(ll i = 0; i < m; i++){cin >> t >> l >> r;q.pb(Mp(t, Mp(l, r)));if(t == 1){b[l - 1] += r;p.pb(b[l - 1]);}}sort(p.begin(), p.end());k = p.size();for(ll i = 0; i < n; i++){upd(1, 0, k, lower_bound(p.begin(), p.end(), a[i]) - p.begin(), 1, a[i]);}for(ll i = 0; i < m; i++){t = q[i].F;l = q[i].S.F;r = q[i].S.S;if(t == 1){upd(1, 0, k, lower_bound(p.begin(), p.end(), a[l - 1]) - p.begin(), -1, -a[l - 1]);a[l - 1] += r;upd(1, 0, k, lower_bound(p.begin(), p.end(), a[l - 1]) - p.begin(), 1, a[l - 1]);}else{cout << solve(lower_bound(p.begin(), p.end(), l) - p.begin(), upper_bound(p.begin(), p.end(), r) - p.begin()) << '\n';}}return 0;
}

平衡树treap:

#include<bits/stdc++.h>
using namespace std;mt19937 rnd(time(0));struct item{int key,prior;int sz;long long sum;long long ans;int l,r;item(){}item(int key):key(key),prior(rnd()),sz(1),ans(0),sum(key),l(0),r(0){}
}tree[200010];int tcnt=0;int new_item(int key){tree[++tcnt]=item(key);return tcnt;
}int cnt(int it){return it?tree[it].sz:0;
}long long sum(int it){return it?tree[it].sum:0LL;
}long long ans(int it){return it?tree[it].ans:0LL;
}void upd(int it){if(it){int l=tree[it].l,r=tree[it].r;tree[it].sz=1+cnt(l)+cnt(r);tree[it].sum=tree[it].key+sum(l)+sum(r);tree[it].ans=1LL*(cnt(l)-cnt(r))*tree[it].key+1LL*(cnt(l)+1)*sum(r)-1LL*(cnt(r)+1)*sum(l)+ans(l)+ans(r);}
}void split(int t,int key,int &l,int &r){if(!t){l=r=0;return;}if(tree[t].key<=key)split(tree[t].r,key,tree[t].r,r),l=t;else split(tree[t].l,key,l,tree[t].l),r=t;upd(t);
}void merge(int &t,int l,int r){if(!l||!r)t=l?l:r;else if(tree[l].prior>tree[r].prior)merge(tree[l].r,tree[l].r,r),t=l;else merge(tree[r].l,l,tree[r].l),t=r;upd(t);
}void join(int &t,int p,int q){if(!p||!q){t=p?p:q;return ;}if(tree[p].prior<tree[q].prior)swap(p,q);int l,r;split(q,tree[p].key-rnd()%2,l,r);join(tree[p].l,tree[p].l,l);join(tree[p].r,tree[p].r,r);t=p;upd(t);
}void erase(int &t,int key){if(!t)return;if(tree[t].key==key){merge(t,tree[t].l,tree[t].r);}else erase(tree[t].key<key?tree[t].r:tree[t].l,key);upd(t);
}long long out(int &t,int l,int r){int lf,seg,rf;split(t,l-1,lf,seg);split(seg,r,seg,rf);long long ans=tree[seg].ans;merge(t,lf,seg);merge(t,t,rf);return ans;
}int main(){freopen("10//in.txt","r",stdin);freopen("10//out.txt","w",stdout);ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int n,m;int root=0;cin>>n;vector<int > X(n);for(int i=0;i<n;i++){cin>>X[i];join(root,root,new_item(X[i]));}cin>>m;for(int i=0;i<m;i++){int op;cin>>op;if(op==1){int p,d;cin>>p>>d;p--;erase(root,X[p]);X[p]+=d;join(root,root,new_item(X[p]));}else {int l,r;cin>>l>>r;long long ans=out(root,l,r);cout<<ans<<endl;}}
}


B. 数列问题

题意

给定一个7个数的数列,其中两个数x1,x2是未知数,给出其中一个未知数x1的值,问另一个未知数x2可以取什么值使得数列可以构造出一个通项公式。

解题思路

任何有限数列都可以使用多项式拟合,所以不管给定的x1是什么,x2都可以是任何值。

换句话说,x1,x2是什么根本就不影响数列是否可以构造一个通项公式。

故输出一个合法的数字即可。(输出2个以上数字或者其他字符则不行)

代码

#include<stdio.h>
int main()
{printf("0");
}


C. 勋总的英语试卷

题意

有若干选择题,有单选或者多选,每次提交答案后,都能得到反馈,即每道题目是否正确,多选题当且仅当所有选项都选对时才会反馈正确,求至多多少次提交可以答对所有题。

思路

假如只有单选题,那提交次数最多为max(每到题目的选项数),如果只有多选题的话,所有的组合情况为 2 n − 1 2^n-1 2n1次,因为至少要选一个选项。(n为max(每道题目的选项数))

代码


#include <bits/stdc++.h>
using namespace std;long long qpow(long long x, long long y) {long long ans = 1;while (y) {if (y & 1)ans = ans * x;y >>= 1;x = x * x;}return ans;
}int main() {ios::sync_with_stdio(false);int n;cin >> n;long long num;char ch;long long cnt1 = 0;long long cnt2 = 0;for (int i = 0; i < n; i++) {cin >> ch >> num;if (ch == 'S') {cnt1 = max(num, cnt1);} else {cnt2 = max(cnt2, num);}}long long ans1 = qpow(2, cnt2)-1;cout << max(ans1, cnt1) << endl;return 0;
}


D. 签到题

题意

输出一个小写字母满足:

  • 这个字母是陕西省英文的第四个字母
  • 这个字母的美国信息交换标准代码是97
  • 这个字母是字符串"ax"和"xa"的LCS(最长公共子序列)

解题思路

  • 陕西省英文:Shaanxi Province
  • 'a’的美国信息交换标准代码(ASCII码)是97
  • 字符串"ax"和"xa"的LCS是’a’或者’x’

代码

#include<stdio.h>
int main()
{printf("a");
}


E. csgo

解题思路

一个很简单的小常识:想求一个多边形的面积,先定一个原点,逆时针沿着多边形的边,每条边计算原点到边的两个端点的向量叉积/2,加起来,就是多边形面积。
图1(黑色线是多边形,红色点是我们定的原点)
图1(黑色线是多边形,红色点是我们定的原点)

图2(浅蓝色叉积大小为正,深蓝色叉积大小为负)
图2(浅蓝色叉积大小为正,深蓝色叉积大小为负)
图3(浅蓝色叉积大小为正,深蓝色叉积大小为负)
图3(浅蓝色叉积大小为正,深蓝色叉积大小为负)
在这里插入图片描述
图4(浅蓝色叉积大小为正,深蓝色叉积大小为负)
在这里插入图片描述
图5(浅蓝色叉积大小为正,深蓝色叉积大小为负)
在这里插入图片描述
图6(浅蓝色叉积大小为正,深蓝色叉积大小为负,先浅加了再深减了,所以那部分面积没有被累加)
因为任意三点不共线,我们可以把任意反恐精英的坐标变成原点,预处理每两个反恐精英的位置和原点组成三角形包含的恐怖分子的数量,记为 d p [ i ] [ j ] dp[i][j] dp[i][j],像面积一样,只是把面积大小换成了包含的恐怖分子的数量,如果原点到两个反恐精英位置的向量叉积是正的就加包含恐怖分子的数量,如果原点到两个反恐精英位置的向量叉积是负的就减包含恐怖分子的数量。如果 O i ⃗ \vec {Oi} Oi O j ⃗ \vec {Oj} Oj 是逆时针方向,则 O j ⃗ \vec {Oj} Oj O i ⃗ \vec {Oi} Oi 是顺时针方向,所以 d p [ i ] [ j ] = − d p [ j ] [ i ] dp[i][j]=-dp[j][i] dp[i][j]=dp[j][i]。复杂度 O ( N 2 M ) O(N^2M) O(N2M)
每次询问,就把反恐精英的点求一次凸包,然后逆时针沿着凸包的边,每个边有两个点,假如边的出发端点是 i i i,边的终止端点是 j j j,把预处理 d p [ i ] [ j ] dp[i][j] dp[i][j]加起来,就是询问的答案。复杂度 O ( ∑ k l o g k + k ) O(∑klogk+k) O(klogk+k)
原题链接:https://www.codechef.com/problems/CHEFPOLY

代码

#include<bits/stdc++.h>
using namespace std;using Point=complex<int >;int dot(Point a,Point b){return (conj(a)*b).real();
}int cross(Point a,Point b){return (conj(a)*b).imag();
}bool belong(Point A,Point B,Point C,Point P){return abs(cross(B-A,C-A))==abs(cross(A-P,B-P))+abs(cross(B-P,C-P))+abs(cross(C-P,A-P));
}int sign(int v){return v==0?0:v>0?1:-1;
}vector<int > getConvex_Hull(const vector<int >& V,const vector<Point >& R){int len=V.size();vector<int > newV(V.begin(),V.end());sort(newV.begin(),newV.end(),[&](int x,int y){return R[x].real()==R[y].real()?R[x].imag()<R[y].imag():R[x].real()<R[y].real();});Point rot[2]={{0,1},{0,-1}};vector<int > hull[2]={{newV[0]},{newV[0]}};for(int k=0;k<2;k++){vector<Point> vers;for(int i=1;i<len;i++){while(vers.size()&&dot(vers.back(),R[newV[i]]-R[hull[k].back()])<=0){hull[k].pop_back();vers.pop_back();}vers.push_back((R[newV[i]]-R[hull[k].back()])*rot[k]);hull[k].push_back(newV[i]);}}reverse(hull[1].begin(),hull[1].end());hull[0].insert(hull[0].end(),hull[1].begin()+1,hull[1].end()-1);return hull[0];
}int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int N,M;cin>>N>>M;vector<Point> R(N),B(M);for(auto &r:R){int x,y;cin>>x>>y;r={x,y};}for(auto &b:B){int x,y;cin>>x>>y;b={x,y};}Point O(R[0]);for(auto &r:R){r-=O;}for(auto &b:B){b-=O;}O={0,0};vector<vector<int > > T(N,vector<int >(N)); for(int i=0;i<N;i++){for(int j=i+1;j<N;j++){int signq=sign(cross(R[i],R[j]));if(signq==0)continue;for(int k=0;k<M;k++){if(belong(O,R[i],R[j],B[k])){T[i][j]+=signq;T[j][i]-=signq;}}}}int Q;cin>>Q;for(int i=0;i<Q;i++){//cerr<<i<<endl;int ans=0;int k;cin>>k;//cerr<<k<<endl;vector<int > V(k); for(auto &v:V){cin>>v;v--;}V=getConvex_Hull(V,R);k=V.size();for(int j=0;j<k;j++){//cerr<<V[j]<<' ';int net=(j+1)%k;ans+=T[V[j]][V[net]];}//cerr<<endl;cout<<abs(ans)<<endl;}


F. Total order

解题思路

样例很明显的暗示,如果是环答案就是 0 0 0,如果不是环那就是树或森林。先按照输入建图,每个同学都连向他想超越的同学(无向图,有向图的话是他想超越的同学指向他自己),如果是自己,则连向一个虚假的最后同学(第 n + 1 n+1 n+1个同学)。假如判断没有环,则这是一棵树,根是那个虚假的同学。
现在我们想要的是一个排列,假如每个子树都代表它能构成排列的数量。
叶子的时候,答案是 1 1 1,只有自己。
当点不是叶子的时候,它会有很多子树(也可能1个),这些子树都已经变成了排列,我们需要把这些排列随意相互合并即可,但原来子树的排列的前后顺序不能交换,然后把点作为最后的同学就形成新的排列。假设现在这个点有k个子树,每个子树大小为 l e n 1 , l e n 2 , l e n 3 , . . . , l e n k len_1,len_2,len_3, ... ,len_k len1,len2,len3,...,lenk,子树都已经变成了排列,则合并种类有 C l e n 1 + l e n 2 + l e n 3 + . . . + l e n k l e n 1 ∗ C l e n 2 + l e n 3 + . . . + l e n k l e n 2 ∗ . . . ∗ C l e n k l e n k C_{len_1+len_2+len_3+...+len_k}^{len_1}*C_{len_2+len_3+...+len_k}^{len_2}*...*C_{len_k}^{len_k} Clen1+len2+len3+...+lenklen1Clen2+len3+...+lenklen2...Clenklenk种,即 ( l e n 1 + l e n 2 + l e n 3 + . . . + l e n k ) ! l e n 1 ! l e n 2 ! l e n 3 ! . . . l e n k ! \frac{(len_1+len_2+len_3+...+len_k)!}{len_1!len_2!len_3!...len_k!} len1!len2!len3!...lenk!(len1+len2+len3+...+lenk)!
(可以看作为从 ( 0 , 0 , 0 , . . . 0 ) (0,0,0,...0) (0,0,0,...0)走到 ( l e n 1 , l e n 2 , l e n 3 , . . . l e n k ) (len1,len2,len3,...lenk) (len1,len2,len3,...lenk),但每次只能移动 ( 1 , 0 , 0 , . . . 0 ) o r ( 0 , 1 , 0 , . . . 0 ) o r ( 0 , 0 , 1 , . . . 0 ) o r . . . o r ( 0 , 0 , 0 , . . . 1 ) (1,0,0,...0)or(0,1,0,...0)or(0,0,1,...0)or...or(0,0,0,...1) (1,0,0,...0)or(0,1,0,...0)or(0,0,1,...0)or...or(0,0,0,...1),的路径数。)。然后这个点和它的子树也是一个子树,这个子树的排列数量为: ( l e n 1 + l e n 2 + l e n 3 + . . . + l e n k ) ! l e n 1 ! l e n 2 ! l e n 3 ! . . . l e n k ! \frac{(len_1+len_2+len_3+...+len_k)!}{len_1!len_2!len_3!...len_k!} len1!len2!len3!...lenk!(len1+len2+len3+...+lenk)!×点的子树1的排列数量×点的子树2的排列数量×点的子树3的排列数量×…×点的子树k的排列数量。
算到根的位置,就是符合要求的全部排列数量。
因为答案要模 1000000007 1000000007 1000000007,所以用线性逆的话复杂度是 O ( n ) O(n) O(n),用快速幂求逆的话复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

代码

#include<bits/stdc++.h>
using namespace std;const long long mod=1e9+7;long long fact[100010];long long fpow(long long a,long long b){long long z=1;while(b){if(b&1)z=z*a%mod;a=a*a%mod;b>>=1;}return z;
}long long inv(long long x){return fpow(x,mod-2);
}vector<int > G[100001];int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);fact[0]=1;for(int i=1;i<=100000;i++){fact[i]=fact[i-1]*i%mod;}int n;cin>>n;vector<int > a(n+1);for(int i=1;i<=n;i++){cin>>a[i];if(a[i]<1||a[i]>n)cerr<<"error"<<endl;}vector<int  > vis(n+1);bool loop=false;for(int i=1;i<=n;i++){if(!vis[i]){int cur=i;do{//cout<<cur<<endl;vis[cur]=i;cur=a[cur];if(cur!=a[cur]&&vis[cur]==i){loop=true;break;}}while(a[cur]!=cur&&vis[cur]==0);}}//cout<<"yes"<<endl;if(loop){//cout<<"loop"<<endl;cout<<0<<endl;return 0;}for(int i=1;i<=n;i++){if(a[i]!=i)G[a[i]].push_back(i);elseG[0].push_back(i);}vector<int > sz(n+1);function<void (int )> dfsz=[&](int u)->void{sz[u]=1;for(int v:G[u]){dfsz(v);sz[u]+=sz[v];}};dfsz(0);function<long long(int ,int )> C=[&](int p,int q)->long long{return fact[p]*inv(fact[q])%mod*inv(fact[p-q])%mod;};function<long long (int )> dfs=[&](int u)->long long{long long ans=1;int subsz=0;for(int v:G[u]){ans=(ans*dfs(v))%mod;subsz+=sz[v];//if(u==0){//cerr<<sz[v]<<endl;//}}for(int v:G[u]){ans=(ans*C(subsz,sz[v]))%mod;subsz-=sz[v];}return ans;};long long ans=dfs(0);cout<<ans<<endl;
}

原题网址:https://ac.nowcoder.com/acm/problem/229386
(原题n≤10,但出题人没想到是的复杂度 n ! n! n!,而且 10 ! 10! 10!居然只有 3 ∗ 1 0 5 3*10^5 3105,所以想到了这个方法,因为考虑到大部分人都不会线性逆,便定 n ≤ 1 0 5 n≤10^5 n105。)



G. 史上最强控分选手LJL

题意

总共有d道题目,最强的LJL每道题目至少都能得一分,最多得f分,所以他每道题目可以随意控制得[1,f]这个区间内的任意一个分数,他想知道有多少可能组成target分。

思路

状态:dp[i][j] 代表 做 i 道题目总分为 j;
方程:dp[i][j] 与 dp[i - 1] 的关系是什么呢? 第 i道题目LJL得了 k ( 1 <= k <= f),那么前 i - 1 道题总分为 j - k,对应 dp[i - 1][j - k];
于是有最终方程:dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j - 2] + … + dp[i - 1][j - f]
边界条件: dp[1][k] = 1 ( 1<= k <= min(target, f) )

代码

#include <bits/stdc++.h>
using namespace std;long long qpow(long long x, long long y) {long long ans = 1;while (y) {if (y & 1)ans = ans * x;y >>= 1;x = x * x;}return ans;
}int solve(int n, int k, int target) {int dp[40][1100];memset(dp, 0, sizeof(int) * 40 * 1100);const int mod = 1e9 + 7;int mm = min(k, target);for (int i = 1; i <= k; i++) {dp[1][i] = 1;}for (int i = 1; i <= n; i++) {for (int j = 1; j <= target; j++) {for (int l = 1; l <= k; l++) {if (j >= l) {dp[i][j] = (dp[i][j] + dp[i - 1][j - l]) % mod;}}}}// dp[i][j] = sum (dp[i-1][j-1],dp[i-1][j-2]...)return dp[n][target];
}int main() {ios::sync_with_stdio(false);int t;cin >> t;while (t--) {int d, f, target;cin >> d >> f >> target;cout << solve(d, f, target) << endl;}return 0;
}

H. 114514

题意

给你一个正整数n,n≤1e7,请你找到最小的n位数x(不允许前导0)满足x同时被11,451,4三个数整除。

解题思路

显而易见,如果x能同时被 11 , 451 , 4 11,451,4 114514三个数整除,则 x x x必然是 l c m ( 11 , 451 , 4 ) lcm(11,451,4) lcm(11,451,4)的倍数( l c m lcm lcm表示最小公倍数)

l c m ( 11 , 451 , 4 ) = 1804 lcm(11,451,4)=1804 lcm(11,451,4)=1804

则问题变成给你一个正整数n,n≤1e7,请你找到最小的n位数x%1804=0。

因此我们计算出 1 0 n − 1 m o d 1804 10^{n-1} mod 1804 10n1mod1804(需要手动进行模拟)将其记为 t t t,因此结果为 1 0 n − 1 + ( 1804 − t ) 10^{n-1}+(1804-t) 10n1+(1804t)

结果很大,无法直接输出。但是我们发现 t < 1804 t<1804 t<1804,所以结果的前 n − 4 n-4 n4位必然为 10000000000000......0000 10000000000000......0000 10000000000000......0000,因此先输出 10000000000000......0000 10000000000000......0000 10000000000000......0000(共 n − 4 n-4 n4位),再输出 t t t即可。

细心的同学可能会发现,如果 t = 0 t=0 t=0 1 0 n − 1 10^{n-1} 10n1本身就算1804的倍数不是就寄了?答案会多加一个1804。
嗯,很有道理,建议再想想。

上述情况需要特判 n < 4 , n = 4 n<4,n=4 n<4,n=4的情况,不过比较简单,不多赘述

代码

#include<stdio.h>
#include<iostream>
using namespace std;
int main()
{int n;scanf("%d",&n);int now=1;for(int i=1;i<n;i++){now=now*10%1804;	}	if(n<=3){printf("xie'e");}else if(n==4){printf("1804");}else{printf("1");for(int i=1;i<=n-5;i++){printf("0");}printf("%04d",1804-now);}
} 


I. 大佬的字符串

题意

对字符串s进行任意次如下两种操作,使得最后得到的字符串字典序最小

  • 把字符串s开头的一个字符放到字符串a的末尾。
  • 把字符串a末尾的一个字符放到字符串b的末尾。

最后s、a都为空

思路

要使得最后得到的字符串字典序最小,显然当字符串a末尾的字符小于等于s串中的最小字符时,就可以把它移到b的末尾。

可以先记录一个后缀最小值,快速判断当前值是否小于等于之后的最小值

代码

#include <bits/stdc++.h>
using namespace std;struct Solution {string getAns(string& str) {vector<char> v(str.size());int size = str.size();v[size - 1] = str[size - 1];int i;for (i = size - 2; i >= 0; i--) {v[i] = min(v[i + 1], str[i]);}string u = "";stack<char> st;// bbadci = 0;while (i < size) {while (st.size() && st.top() <= v[i]) {u += st.top();st.pop();}st.push(str[i]);i++;}while (st.size()) {u += st.top();st.pop();}return u;}
};int main() {ios::sync_with_stdio(false);string s;cin >> s;Solution ss;cout << ss.getAns(s) << endl;return 0;
}


J. 特殊的合并

解题思路

因为哈夫曼树的带权路径长度最小,所以是哈夫曼裸题,只需要优先队列就可以算出最小值,然后比较和M的大小即可。

代码

#include<bits/stdc++.h>
using namespace std;int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int T;cin>>T;while(T--){int n;cin>>n;priority_queue<int , vector<int > ,greater<int > > Q;for(int i=0;i<n;i++){int a;cin>>a;Q.push(a);}int sum=0;while(Q.size()>1){int a,b;a=Q.top();Q.pop();b=Q.top();Q.pop();sum+=a+b;Q.push(a+b);}int m;cin>>m;if(m>=sum){cout<<"Yes"<<endl;}else{cout<<"No"<<endl;}}
}

启发题链接:https://codeforces.com/problemset/problem/958/F3
在这里插入图片描述



这篇关于第七届集美大学程序设计竞赛(个人赛)题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/236532

相关文章

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

C - Word Ladder题解

C - Word Ladder 题解 解题思路: 先输入两个字符串S 和t 然后在S和T中寻找有多少个字符不同的个数(也就是需要变换多少次) 开始替换时: tips: 字符串下标以0开始 我们定义两个变量a和b,用于记录当前遍历到的字符 首先是判断:如果这时a已经==b了,那么就跳过,不用管; 如果a大于b的话:那么我们就让s中的第i项替换成b,接着就直接输出S就行了。 这样

C语言程序设计(数据类型、运算符与表达式)

一、C的数据类型 C语言提供的数据类型: 二、常量和变量 2.1常量和符号常量 在程序运行过程中,其值不能被改变的量称为常量。 常量区分为不同的类型: 程序中用#define(预处理器指令)命令行定义变量将代表常量,用一个标识符代表一个常量,称为符合常量。 2.2变量 变量代表内存中具有特定属性的一个存储单元,用来存放数据,在程序运行期间,这些值是可以 改变的。 变

【秋招笔试】9.07米哈游秋招改编题-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收集 100+ 套笔试题,笔试真题 会在第一时间跟新 🍄 题面描述等均已改编,如果和你笔试题看到的题面描述

C语言程序设计(选择结构程序设计)

一、关系运算符和关系表达式 1.1关系运算符及其优先次序 ①<(小于) ②<=(小于或等于) ③>(大于) ④>=(大于或等于 ) ⑤==(等于) ⑥!=(不等于) 说明: 前4个优先级相同,后2个优先级相同,关系运算符的优先级低于算术运算符,关系运算符的优先级高于赋值运算符 1.2关系表达式 用关系运算符将两个表达式(可以是算术表达式或关系表达式,逻辑表达式,赋值表达式,字符

LeetCode 第414场周赛个人题解

目录 Q1. 将日期转换为二进制表示 原题链接 思路分析 AC代码 Q2. 范围内整数的最大得分 原题链接 思路分析 AC代码 Q3. 到达数组末尾的最大得分 原题链接 思路分析 AC代码 Q4. 吃掉所有兵需要的最多移动次数 原题链接 思路分析 AC代码 Q1. 将日期转换为二进制表示 原题链接 Q1. 将日期转换为二进制表示 思路分析

2024年AMC10美国数学竞赛倒计时两个月:吃透1250道真题和知识点(持续)

根据通知,2024年AMC10美国数学竞赛的报名还有两周,正式比赛还有两个月就要开始了。计划参赛的孩子们要记好时间,认真备考,最后冲刺再提高成绩。 那么如何备考2024年AMC10美国数学竞赛呢?做真题,吃透真题和背后的知识点是备考AMC8、AMC10有效的方法之一。通过做真题,可以帮助孩子找到真实竞赛的感觉,而且更加贴近比赛的内容,可以通过真题查漏补缺,更有针对性的补齐知识的短板。