首届保定学院大学生程序设计大赛-正式赛(校内)AK
作为隔壁学校打星队伍参赛,封榜n+2,开出M题和最后的大模拟,拿下校内赛全场rk1,虽然题目整体比较简单,但有几个题出的是真不错,学到不少东西
赛时前两个半小时交给队友写的,十一点左右才起床上线。
只写自己做的题的详细题解
F 《赚钱养活自己》
dp题,设f[i]表示在前i个区间内选择,第i个区间可选可不选,的收益最大值
若不选第i段区间,则可以从f[i-1]转移过来
若选第i段区间,则需要找到前面最近的跟这一段不冲突的区间,这一步可以二分的找到。
注意这里的的区间是左开右闭的[l,r)
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;
struct seg{
int st,ed,w;
bool operator<(const seg &s)const{
return ed<s.ed;
}
};
void solve(){
int n;
cin>>n;
vector<seg> a(n+1);
for(int i=1;i<=n;i++){
int u,v,w;
cin>>u>>v>>w;
a[i]={u,v,w};
}
sort(a.begin()+1,a.end());
vector<int> f(n+10);
vector<int> endd(n+1);
for(int i=1;i<=n;i++){
endd[i]=a[i].ed;
}
for(int i=1;i<=n;i++){
auto [l,r,w]=a[i];
//找到第一个r大于当前l的位置,往前挪一个就是正确位置
int pre=upper_bound(endd.begin()+1,endd.end(),l)-endd.begin();
pre--;
f[i]=max(f[i-1],f[pre]+w);
}
cout<<f[n]<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--){
solve();
}
return 0;
}
G 星际旅行
dijkstra板子题,只要在建边的时候,把起始星球的ei加进这条边的边权中即可
然后跑dijkstra就行
实现可以看代码
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n,m;
cin>>n>>m;
vector<vector<pii>> g(n+10);
vector<int> e(n+10);
for(int i=1;i<=n;i++){
cin>>e[i];
}
while(m--){
int u,v,w;
cin>>u>>v>>w;
g[u].push_back({v,w+e[u]});
g[v].push_back({u,w+e[v]});
}
vector<int> dist(n+10,inf),st(n+10);
auto dijkstra = [&]()-> void {
dist[1]=0;
priority_queue<pii,vector<pii>,greater<pii>> q;
q.push({0,1});
while(q.size()){
auto t=q.top();
q.pop();
int u=t.second;
if(st[u]) continue;
st[u]=1;
for(auto ne:g[u]){
int v=ne.first,w=ne.second;
if(dist[v]>dist[u]+w){
dist[v]=dist[u]+w;
q.push({dist[v],v});
}
}
}
};
dijkstra();
if(dist[n]>inf/2){
cout<<-1;
}
else cout<<dist[n];
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--){
solve();
}
return 0;
}
I 最长合规灌溉区域
说实话这题读题理解题意花了不少时间
且数据范围是1e7
一开始写了个st表维护的二分,复杂度nlogn,以为能过,结果tle了两发,才发现数据范围是1e7不是1e5
正解显然是on的
需要找一个连续的序列,使得序列综合不超过s,且序列最大值不超过c。
使用双指针维护滑动窗口即可,在每次加进新元素时,判定新元素值不超过c。
加进去后,如果总和超过s,则从窗口尾部弹出元素。
每次找到一个合法的l,r,则更新答案
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
ll s;
int c;
cin>>n>>s>>c;
vector<int>a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
ll sum=0;
int l=1,ans=0;
for(int r=1;r<=n;r++){
if(a[r]>c){
sum=0;
l=r+1;
continue;
}
sum+=a[r];
while(l<=r&&sum>s){
sum-=a[l++];
}
ans=max(ans,r-l+1);
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
return 0;
}
K 初夏!迷宫?大探险!
很板的换根dp,很适合没接触过或写过但不知道换根dp是什么的同学了解学习换根dp的思想
先假设0号节点为根,计算出以0号节点为根时的ans。
继续考虑,当前的根节点是u(初始是0),要计算根节点是u的子节点v时的答案。
根节点从u到v,ans会有什么变化?
v这颗子树上,所有节点的距离会-1
其他节点的距离会+1
所以根节点从u到v时,只需要:
ans-=sz[v]
ans+=(n-sz[v])
其中sz[v]表示以v为根的子树大小
两遍dfs,第一遍计算出sz,dep数组。
第二遍换根dp计算答案
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
cin>>n;
vector<vector<int>> g(n+10);
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> sz(n+10),dep(n+10);
auto dfs = [&](auto dfs,int u,int pre)-> void {
sz[u]=1;
for(auto v:g[u]){
if(v==pre) continue;
dep[v]=dep[u]+1;
dfs(dfs,v,u);
sz[u]+=sz[v];
}
};
dfs(dfs,0,-1);
vector<int> ans(n+10);
int now=0;
for(int i=1;i<=n;i++){
now+=dep[i];
}
auto dfs1 = [&](auto dfs1, int u, int pre)->void {
ans[u]=now;
for(auto v:g[u]){
if(v==pre) continue;
now-=sz[v];
now+=(n-sz[v]);
dfs1(dfs1,v,u);
now+=sz[v];
now-=(n-sz[v]);
}
};
dfs1(dfs1,0,-1);
for(int i=0;i<n;i++){
cout<<ans[i]<<" ";
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--){
solve();
}
return 0;
}
L 古文字破译
数据范围1000,赛时感慨出的题真好,好奇妙的trie维护dp
结果一看题解,用substr写了个n^3的假做法()
先把所有词典中的词放进trie树中,同时对trie树的每个节点,标记是否是一个单词的结尾
dp[i]表示,从第1个字符开始,前i-1个字符能否全部表示出来
所以最后如果dp[n+1] = 1,则输出Y,否则输出N
(假设字符串下标从1开始,1idx)
初始状态是dp[1] = 1
i从1到n,如果dp[i] = 1,则:
枚举j = [i,n]
我们要找到所有[i,j],满足si ~ sj 是一个出现过的单词
在trie树中,开始找si ~ sn,假设当前在j
如果sj存在trie树中,且sj所在的节点不是一个单词的结尾,则继续,j++
如果sj存在trie树中,且sj所在的节点是一个单词的结尾,则si - sj 可以被表示出来,且之前的s1-s(i-1)也可以被表示出来,则更新dp[j+1]=1, 然后继续,j++
如果sj不存在trie树中,则break,停止内层循环
代码还是比较好写,主要是trie树记录每个节点是否是某个单词的结尾
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii = pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
struct Trie {
vector<array<int,26>> nxt;
vector<bool> isEnd;
Trie(){
nxt.push_back(array<int,26>{});
isEnd.push_back(false);
for(int i=0;i<26;i++) nxt[0][i] = -1;
}
void insert(const string &s){
int u = 0;
for(char ch: s){
int c = ch - 'a';
if(nxt[u][c] == -1){
nxt[u][c] = nxt.size();
nxt.push_back(array<int,26>{});
isEnd.push_back(false);
for(int k=0;k<26;k++) nxt.back()[k] = -1;
}
u = nxt[u][c];
}
isEnd[u] = true;
}
};
void solve(){
string s;
cin>>s;
int n;
cin>>n;
Trie tr;
for(int i=1;i<=n;i++){
string tmp;
cin>>tmp;
tr.insert(tmp);
}
n=s.size();
s=" "+s;
vector<int> f(n+10);//f[i]: 1-(i-1)能被匹配上
f[1]=1;
for(int i=1;i<=n;i++){
if(!f[i]) continue;
int now=0;
for(int j=i;j<=n;j++){
int ch=s[j]-'a';
if(tr.nxt[now][ch]==-1) break;
now=tr.nxt[now][ch];
if(tr.isEnd[now]){
f[j+1]=1;
}
}
}
if(f[n+1]) cout<<"Y";
else cout<<"N";
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
return 0;
}
M 翻倍!数组?大积分!
重点是如何o(n)的找到每个数的质因数个数
首先,先找到从1到n,每个数的最小质因数lp
再dp的,从1到n, 对每个a[i],找到a[i]/lp = y
如果y的最小质因数跟a[i]的一样,则a[i]的质因数个数等于y的
否则a[i]的最小质因数个数等于y的个数+1
这一步的代码:
vector<int> lp(mx+1),ps(mx+1);
vector<int> primes;
for(int i=2;i<=mx;i++){
if(!lp[i]){
lp[i] = i;
primes.push_back(i);
}
for(int p:primes){
if(p>lp[i]||i*p>mx) break;
lp[i*p]=p;
}
}
ps[1]=0;
for(int x=2;x<=mx;x++){
int p=lp[x];
int y=x/p;
if(lp[y]==p) ps[x]=ps[y];
else ps[x]=ps[y]+1;
}
找到每个数的质数分数后,下一步要找到,对于每一个a[i],会有多少个区间,的权值是a[i]
对于左边界L: i左边第一个满足质数分数 >= i的质数分数的位置
对于有边界R:i右边第一个满足质数分数 > i的质数分数的位置
找到lr后,以a[i]为答案的区间个数为 cnt =(i-l) * (r-i)
计算出每一个(a[i], cnt), 按照a[i] 从大到小排序
每次贪心的取最大的a[i]即可
思路比较好想到,但实现比较复杂,尤其是o(n)求质因数个数
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 1e9+7;
int qmi(int a,int b,int p){
int res=1;
while(b){
if(b&1) res=res*a%p;
b>>=1;
a=a*a%mod;
}
return res;
}
void solve(){
int n,k;
cin>>n;
vector<int> a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
cin>>k;
int mx=*max_element(a.begin()+1,a.end());
vector<int> lp(mx+1),ps(mx+1);
vector<int> primes;
for(int i=2;i<=mx;i++){
if(!lp[i]){
lp[i] = i;
primes.push_back(i);
}
for(int p:primes){
if(p>lp[i]||i*p>mx) break;
lp[i*p]=p;
}
}
ps[1]=0;
for(int x=2;x<=mx;x++){
int p=lp[x];
int y=x/p;
if(lp[y]==p) ps[x]=ps[y];
else ps[x]=ps[y]+1;
}
vector<int> score(n+1);
for(int i=1;i<=n;i++){
score[i]=ps[a[i]];
}
vector<int> l(n+10),r(n+10);
vector<int> st;
for(int i=1;i<=n;i++){
while(!st.empty() && score[st.back()] < score[i]) st.pop_back();
if(st.empty()) l[i]=0;
else l[i]=st.back();
st.push_back(i);
}
st.clear();
for(int i=n;i>=1;i--){
while(!st.empty() && score[st.back()] <= score[i]) st.pop_back();
if(st.empty()) r[i]=n+1;
else r[i]=st.back();
st.push_back(i);
}
vector<pii> t;
for(int i=1;i<=n;i++){
int cnt=(i-l[i])*(r[i]-i);
if(cnt>0){
t.push_back({a[i], cnt});
}
}
sort(t.begin(),t.end(),greater<>());
int ans=1;
for(auto [val,cnt]:t){
if(k<=0) break;
int use = min(cnt, k);
ans=ans*qmi(val,use,mod)%mod;
k-=use;
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--){
solve();
}
return 0;
}
剩下的部分是队友写的,只有代码
A WelcomeToBDU
点击查看代码
//ZenithHacker
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5+10;
const ll INF=2e18+10;
void solve(){
cout<<"Ciallo~(yigeyanwenzi)\\%Welcome to BduCPC!";
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
B ❤书页之情❤
点击查看代码
//ZenithHacker
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5+10;
const ll INF=2e18+10;
void solve(){
ll x;
cin>>x;
if(x>0&&x%2){
cout<<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
return 0;
}
C ❤爱的序列❤
点击查看代码
//ZenithHacker
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5+10;
const ll INF=2e18+10;
map<char,int>m;
void solve(){
string s="baodingxueyuan";
for(int i=0;i<s.size();i++){
m[s[i]]=1;
}
int n;
cin>>n;
string str;
cin>>str;
int flag=0;
for(int i=0;i<str.size();i++){
if(m[str[i]]==1){
flag=1;
}
}
if(flag){
cout<<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
return 0;
}
D ❤爱的深沉❤
点击查看代码
//ZenithHacker
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5+10;
const ll INF=2e18+10;
ll a[N];
void solve(){
int a1,d,n;
cin>>a1>>d>>n;
a[1]=a1;
for(int i=2;i<=n;i++){
a[i]=a[i-1]+d;
}
for(int i=1;i<=n;i++){
a[i]+=a[i-1];
}
int q;
cin>>q;
for(int i=1;i<=q;i++){
int x,y;
cin>>x>>y;
cout<<a[y]-a[x-1]<<endl;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
E 啊哈哈哈哈鸡汤来喽!
点击查看代码
//ZenithHacker
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5+10;
const ll INF=2e18+10;
void solve(){
ll x;
cin>>x;
int cnt=0;
while(x){
x>>=1;
cnt++;
}
cout<<cnt<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
H 拼接字符串
点击查看代码
//ZenithHacker
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5+10;
const ll INF=2e18+10;
void solve(){
string a,b,c;
cin>>a>>b>>c;
transform(a.begin(),a.end(),a.begin(),::tolower);
transform(b.begin(),b.end(),b.begin(),::tolower);
transform(c.begin(),c.end(),c.begin(),::tolower);
for(int i=0;i<a.size();i++){
if(a[i]=='-'||a[i]=='_'||a[i]==';'){
a.erase(i,1);
i--;
}
}
for(int i=0;i<b.size();i++){
if(b[i]=='-'||b[i]=='_'||b[i]==';'){
b.erase(i,1);
i--;
}
}
for(int i=0;i<c.size();i++){
if(c[i]=='-'||c[i]=='_'||c[i]==';'){
c.erase(i,1);
i--;
}
}
string s1=a+b+c,s2=a+c+b,s3=b+a+c,s4=b+c+a,s5=c+a+b,s6=c+b+a;
int q;
cin>>q;
while(q--){
string s;
cin>>s;
transform(s.begin(),s.end(),s.begin(),::tolower);
for(int i=0;i<s.size();i++){
if(s[i]=='-'||s[i]=='_'||s[i]==';'){
s.erase(i,1);
i--;
}
}
if(s==s1||s==s2||s==s3||s==s4||s==s5||s==s6){
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
J 生成新数
点击查看代码
//ZenithHacker
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5+10;
const ll INF=2e18+10;
vector<vector<int>>a(20);
int s[10],vis[10];
int dfs(int x){
vis[x]=1;
if(a[x].empty()){
return 1;
}
int sum=1,flag=0;
for(int v:a[x]){
if(!vis[v]){
sum+=dfs(v);
flag=1;
}
}
if(!flag)return 1;
return sum;
}
void solve(){
int n,k;
cin>>n>>k;
while(k--){
int x,y;
cin>>x>>y;
a[x].push_back(y);
}
for(int i=0;i<=9;i++){
memset(vis,0,sizeof vis);
s[i]=dfs(i);
//cout<<s[i]<<endl;
}
int sum=1;
while(n){
sum*=s[n%10];
n/=10;
}
cout<<sum<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
N 逻辑表达式的成真赋值
本来以为最后要rk2了,没想到队友居然把⑩吃出来了
点击查看代码
//ZenithHacker
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 1e5+10;
const ll INF=2e18+10;
map<char,int>mp;
vector<char>x;
void solve(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
char c;
cin>>c;
mp[c]=1;
x.push_back(c);
}
string s,str;
cin>>s;
stack<char>st;
for(int i=0;i<s.size();i++){
if(mp.count(s[i])){
str+=s[i];
}
else if(s[i]=='('){
st.push(s[i]);
}
else if(s[i]==')'){
while(st.size()&&st.top()!='('){
str+=st.top();
st.pop();
}
st.pop();
}
else{
while(st.size()&&st.top()!='('){
str+=st.top();
st.pop();
}
st.push(s[i]);
}
}
while(st.size()){
str+=st.top();
st.pop();
}
//cout<<str<<endl;
for(int i=0;i<pow(2,n);i++){
int p=i;
for(int j=n-1;j>=0;j--){
mp[x[j]]=p&1;
//cout<<(p&1);
p>>=1;
}
//cout<<endl;
for(int j=0;j<str.size();j++){
if(mp.count(str[j])){
st.push(mp[str[j]]+'0');
}
else if(str[j]=='1'){
if(st.top()=='1'){
st.top()='0';
}
else
st.top()='1';
}
else if(str[j]=='2'){
char tmp=st.top();
st.pop();
if(st.top()=='0'&&tmp=='0'){
st.top()='0';
}
else{
st.top()='1';
}
}
else if(str[j]=='3'){
char tmp=st.top();
st.pop();
if(st.top()=='1'&&tmp=='1'){
st.top()='1';
}
else{
st.top()='0';
}
}
else{
char tmp=st.top();
st.pop();
if(st.top()=='1'&&tmp=='0'){
st.top()='0';
}
else{
st.top()='1';
}
}
}
if(st.top()=='1'){
for(int j=0;j<n;j++){
cout<<mp[x[j]];
}
cout<<endl;
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}