首届保定学院大学生程序设计大赛-正式赛(校内)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;
}
posted @ 2025-05-25 22:27  LYET  阅读(69)  评论(0)    收藏  举报