A.做游戏

原题链接 解题思路

尽可能多的使牛牛获胜,那么出石头,剪刀,布三种都取获胜的最大可能,对于石头:牛牛出的石头数量与牛可乐出的剪刀数量,剪刀和布亦然 note:注意c++开 long long

code

void solve(){
    ll a,b,c;
    ll x,y,z;
    cin>>a>>b>>c;
    cin>>x>>y>>z;
    cout<<min(a,y)+min(b,z)+min(c,x)<<endl;
}

B.排数字

原题链接

解题思路

贪心的数字串中的616全部提出来,然后以61616...的顺序排下去,发现答案是 min((6的数量-1),1的数量)。

code

void solve(){
    int n;cin>>n;
    string s;cin>>s;
    vector<int>mp(10);
    for(char c:s) mp[c-'0']++;
    cout<<max(min(mp[6]-1,mp[1]),0)<<endl;
}

C.判正误

原题链接

解题思路

快速幂,没啥好说的,对于单一模数冲突概率很大,本题需要采用多模技术,但是题主赛时单模1234567891水过去了

note:快速幂需要处理负数幂不方便,要先把所有数归一化为0~mod-1内的数 ,0^0

code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
vector<ll>Mod={998244353,2,3,5,11,13,1234567891LL};
ll q_pow(ll a,ll b,ll mod){
    a=(a%mod+mod)%mod;
    ll s=1;
    while(b){
        if(b&1){
            s=s*a%mod;
        }
        b>>=1;
        a=a*a%mod;
    }
    return s;
}
void solve(){
    ll a,b,c,d,e,f,g;
    cin>>a>>b>>c>>d>>e>>f>>g;
    bool F=true;
    for(ll mod:Mod){
        ll s1=q_pow(a,d,mod),s2=q_pow(b,e,mod),s3=q_pow(c,f,mod);
        ll s=((s1+s2)%mod+s3)%mod;
        F&=(s==(g%mod+mod)%mod);
    }
    if(F) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
}
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    int T=1;cin>>T;
    while(T--) solve();
    return 0;
}

D.数三角

原题链接

解题思路

暴力枚举三个点 然后用向量叉积判定是否共线,再根据余弦定理或者向量点积判定是否存在钝角。

code

void solve(){
    int n;cin>>n;
    vector< pair<int,int> >res(n+1);
    for(int i=1;i<=n;i++) cin>>res[i].first>>res[i].second;
    auto dis=[&](ll x,ll y) ->ll { return 1LL*x*x+1LL*y*y;};
    auto judge=[&](pair<int,int>a,pair<int,int>b,pair<int,int>c) ->bool {
        pair<int,int>w={b.first-a.first,b.second-a.second};
        pair<int,int>t={c.first-a.first,c.second-a.second};
        return 1LL*w.first*t.second!=1LL*w.second*t.first;
    };
    ll ans=0;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            for(int k=j+1;k<=n;k++){
                if(!judge(res[i],res[j],res[k])) continue;//不共线的判定
                ll a=dis(res[i].first-res[j].first,res[i].second-res[j].second);
                ll b=dis(res[i].first-res[k].first,res[i].second-res[k].second);
                ll c=dis(res[k].first-res[j].first,res[k].second-res[j].second);
                ans+=(a+b<c);
                ans+=(b+c<a);
                ans+=(a+c<b);
            }
        }
    }
    cout<<ans<<endl;
}

E.做计数

原题链接

解题思路

而要满足这个等式存在两个条件:

1.

2. 是一个完全平方数

那么就转化为了求解小于等于 n 范围内的完全平方数配对数了,暴力枚举作为平方的数和其中一个因子即可,复杂度

note:本题可以通过筛做到更快

code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
vector<int>p;
void Init(int n){
    for(int i=1;i<=n/i;i++){
        p.push_back(i*i);
    }
}
void solve(){
    ll n;cin>>n;
    int ans=0;
    for(auto &x:p){
        for(int i=1;i<=x/i;i++){
            if(x>n) break;
            if(x%i==0){
                ans+=(x/i!=i)*2+(x/i==i);
            }
        }
    }
    cout<<ans<<endl;
}
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    Init(4e7+5);
    solve();
    return 0;
}

F.拿物品

原题链接

解题思路

简单贪心,考虑他们在争夺什么?当拿走第 i 个位置的物品后可以使 对手 永远 无法获得 的价值,并且自己一定可以获得 ,那么按照 排序后顺序贪心取得的就是最大值。

code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
struct node{
    int x,y,idx;
};
void solve(){
    int n;cin>>n;
    vector<int>a(n+1);
    vector<int>b(n+1);
    vector< node >res(n+1);
    for(int i=1;i<=n;i++) cin>>a[i],res[i].x=a[i],res[i].idx=i;
    for(int i=1;i<=n;i++) cin>>b[i],res[i].y=b[i];
    sort(res.begin()+1,res.end(),[&](node w,node t){
        return w.x+w.y>t.x+t.y;
    });
    queue<int>Q;
    for(int i=1;i<=n;i++) Q.push(res[i].idx);
    vector<int>ans1,ans2;
    bool cur=0;
    while(!Q.empty()){
        cur^=1;
        int u=Q.front();
        Q.pop();
        (cur)?(ans1.push_back(u)):(ans2.push_back(u));
    }
    for(auto &x:ans1) cout<<x<<' ';
    cout<<endl;
    for(auto &x:ans2) cout<<x<<' ';
    cout<<endl;
}
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    solve();
    return 0;
}

C.算概率

原题链接

解题思路

简单概率dp,考虑前 i 个位置时 答对了 j 个 题目概率由 考虑前 i-1 个位置 答对了 j-1个题目或 j 个题目转移而来 暴力 转移即可 注意特判 n=0 的情况 note:感觉应该基础场C>提高场C

code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int mod=1e9+7;
ll q_pow(ll a,ll b){
    ll s=1;
    while(b){
        if(b&1){
            s=s*a%mod;
        }
        a=a*a%mod;
        b>>=1;
    }
    return s;
}
void solve(){
    int n;cin>>n;
    vector<int>a(n+1);
    vector<ll>p(n+1);
    p[0]=1;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
        vector<ll>nxtp(n+1);
        nxtp[0]=p[0]*(1-a[i]+mod)%mod;
        for(int j=1;j<=i;j++){
            nxtp[j]=(p[j-1]*a[i]%mod+p[j]*(1-a[i]+mod)%mod)%mod;
        }
        p=nxtp;
    }
    for(int i=0;i<=n;i++) cout<<p[i]<<' ';
    cout<<endl;
}
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    solve();
    return 0;
}

D.施魔法

原题链接

解题思路

很容易注意到排序后可以使用区间 dp,前 i 个位置最少消耗魔力 由比 i 小的位置转移过来,具体方程为 ,朴素枚举显然时 的,仔细注意一下发现把 这个东西看成整个整体,每次枚举更新他的前缀最值,最后就可以得到一个 的正解了

code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
void solve(){
    int n,k;cin>>n>>k;
    vector<int>a(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    vector<ll>dp(n+1,INT_MAX);
    dp[0]=0;
    sort(a.begin()+1,a.end());
    ll minl=INT_MAX;
    for(int i=1;i<=n;i++){
        if(i>=k) minl=min(minl,dp[i-k]-a[i-k+1]);
        dp[i]=min(dp[i],minl+a[i]);
    }
    cout<<dp[n]<<endl;
}
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    solve();
    return 0;
}

E.建通道

原题链接

解题思路

位运算优先考虑拆位,发现如果存在某一位点权在序列中至少有一个数与他不同,那么我们可以把,记这一位为 i, 那么我们把所有 第 i 为1的数字与某个第 i 位为0 的数字 相连,再把所有第 i 为 0 的数字与某个第 i 位 为 1 的数字相连, 发现相连后一定一颗生成树,那么就有一个贪心的思路,从小到大枚举 发现第一位序列中有一位数与其不同时,就产生 1<<i*(n-1)的代价,可以证明这样代价一定最小,注意去重,因为 如果两个数相同连接他们产生的 lowbit 显然是 0.

code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
void solve(){
    int n;
    cin>>n;
    vector<int>a(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a.begin()+1,a.end());
    a.erase(unique(a.begin()+1,a.end()),a.end());
    n=a.size()-1;
    for(int j=0;j<=31;j++){
        bool bit=(a[1]>>j&1);
        for(int i=2;i<=n;i++){
            if(bit^(a[i]>>j&1)) {
                cout<<(1LL<<j)*(n-1)<<endl;
                return;
            }
        }
    }
    cout<<0<<endl;
}
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    solve();
    return 0;
}

F.求函数

原题链接

解题思路

区间线段信息合并类题目,可以考虑线段树,发现其由一颗单点更新的线段树可以解决,考虑如何由子节点更新父节点 发现分别维护 k 与 b 即可 由子节点上传信息时

1.

2.

最后输出 k与 b之和即可

note:注意输入格式

code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define lc (p<<1)
#define rc (p<<1|1)
typedef long long ll;
const int mod=1e9+7;
struct Seg{
    struct S{
        int l,r;
        ll k,b;
    };
    int n;
    vector<S>tre;
    Seg(int n):n(n){
        tre.resize(n<<2);
        build(1,n,1);
    }
    pair<ll,ll> merge(pair<ll,ll>x,pair<ll,ll>y){
        return pair<ll,ll>{x.first*y.first%mod,(y.first*x.second%mod+y.second)%mod};
    }
    void pushup(int p){
        tre[p].k=tre[lc].k*tre[rc].k%mod;
        tre[p].b=(tre[rc].k*tre[lc].b%mod+tre[rc].b)%mod;
    }
    void build(int l,int r,int p){
        tre[p]={l,r,0,0};
        if(l==r) return;
        int mid=l+r>>1;
        build(l,mid,lc);
        build(mid+1,r,rc);
        pushup(p);
    }
    void upd(int idx,pair<ll,ll>k,int p=1){
        if(tre[p].l==tre[p].r){
            tre[p].k=k.first%mod;
            tre[p].b=k.second%mod;
            return;
        }
        int mid=tre[p].l+tre[p].r>>1;
        if(mid>=idx) upd(idx,k,lc);
        if(mid<idx) upd(idx,k,rc);
        pushup(p);
    }
    pair<ll,ll> qry(int l,int r,int p=1){
        if(l<=tre[p].l&&r>=tre[p].r){
            return {tre[p].k,tre[p].b};
        }
        pair<ll,ll>L={1,0};
        pair<ll,ll>R={1,0};
        int mid=tre[p].l+tre[p].r>>1;
        if(mid>=l) L=qry(l,r,lc);
        if(mid<r) R=qry(l,r,rc);
        return merge(L,R);
    }   
};
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    int n,m;cin>>n>>m;
    vector< pair<ll,ll> >res(n);
    Seg S(n);
    for(auto &[x,y]:res) cin>>x;
    for(auto &[x,y]:res) cin>>y;
    for(int i=0;i<n;i++) S.upd(i+1,res[i]);
    while(m--){
        int op,idx;
        ll k,b;
        cin>>op;
        if(op==1){
            cin>>idx>>k>>b;
            S.upd(idx,pair<ll,ll>{k,b});
        }
        else {
            cin>>k>>b;
            pair<ll,ll>w=S.qry(k,b);
            cout<<(w.first+w.second)%mod<<endl;
        }
    }
    return 0;
}

新人第一次在牛客写题解,更差的阅读体验:我的博客