zoukankan      html  css  js  c++  java
  • 牛客练习赛80 题解

    A题

    我写的是找是否存在两个1之间有0,并且要特判单一个1的情况

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e5+10;
    const int mod=1e9+7;
    int main(){
        ios::sync_with_stdio(false);
        string s;
        cin>>s;
        int i;
        int cnt=0;
        int flag=0;
        for(i=0;i<s.size();i++){
            if(s[i]=='1'){
                flag=i+1;
                break;
            }
        }
        if(!flag){
            cout<<0<<endl;
        }
        else{
            for(i=0;i<s.size();i++){
                if(s[i]=='1')
                    cnt++;
            }
            if(cnt==1){
                cout<<0<<endl;
                return 0;
            }
            if(cnt==s.size()){
                cout<<1<<endl;
                return 0;
            }
            cnt=0;
            flag=0;
            s=s+"0";
            for(i=flag;i<s.size();i++){
                if(s[i]=='0'&&s[i-1]=='1'){
                    cnt++;
                    if(i<s.size()-1&&s[i+1]=='1'){
                        flag=1;
                    }
                }
            }
            int tmp=0;
            for(i=flag;i<s.size();i++){
                if(i>0&&s[i]=='1'&&s[i-1]=='0'&&s[i+1]=='0'){
                    flag=1;
                    break;
                }
            }
            cout<<cnt-flag<<endl;
        }
        return 0;
    }
    View Code

    B题

    看出规律,只有几种简单的情况,先维护一个和,之后加加减减

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e5+10;
    const ll mod=998244353;
    ll a[N],b[N];
    int main(){
        ios::sync_with_stdio(false);
        ll res=0,ans=0;
        int i;
        int n;
        cin>>n;
        for(i=0;i<n;i++){
            cin>>a[i];
            res+=a[i];
            res%=mod;
        }
        for(i=0;i<n;i++){
            cin>>b[i];
            ans+=b[i];
            ans%=mod;
        }
        for(i=0;i<3;i++){
            if(i==0){
                cout<<((res-a[0]+mod)%mod)*((ans-b[0]+mod)%mod)%mod<<" ";
            }
            else if(i==1){
                ll tmp1=(b[0]%mod*(res-a[1]+mod))%mod;
                ll tmp2=(a[0]%mod*(ans-b[1]+mod))%mod;
                ll tmp3=a[0]*b[0]%mod;
                cout<<(tmp1+tmp2-tmp3+mod)%mod<<" ";
            }
            else if(i==2){
                ll tmp1=a[0]*b[1]%mod+b[0]*a[1]%mod;
                cout<<tmp1%mod<<" ";
            }
        }
        for(i=3;i<n;i++)
            cout<<0<<" ";
        return 0;
    }
    View Code

    C题

    定义状态为f[i][j],i位,第一位以j开头,这样就能列出转移方程,使用矩阵快速幂优化

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e5+10;
    const int mod=100019;
    struct node{
        int a[10][10];
        node(){
            memset(a,0,sizeof a);
        }
    }S,T;
    node operator *(node a,node b) {
        node tmp;
        for(int i=0;i<=9;i++) {
            for(int j=0;j<=9;j++) {
                for(int k=0;k<=9;k++) {
                    tmp.a[i][j]=((ll)tmp.a[i][j]+(ll)a.a[i][k]*b.a[k][j]%mod)%mod;
                }
            }
        }
        return tmp;
    }
    node martix_pow(node s,ll b){
        int i;
        node tmp;
        for(i=0;i<10;i++){
            tmp.a[i][i]=1;
        }
        while(b){
            if(b&1)
                tmp=tmp*s;
            s=s*s;
            b>>=1;
        }
        return tmp;
    }
    int main(){
        ios::sync_with_stdio(false);
        int i;
        int n;
        cin>>n;
        for(i=1;i<=9;i++){
            S.a[0][i]=1;
        }
        for(i=0;i<10;i++){
            for(int j=0;j<10;j++){
                if(j<=i){
                    T.a[i][j]=1;
                }
            }
        }
        T=martix_pow(T,n-1);
        S=S*T;
        ll ans=0;
        for(i=1;i<=9;i++){
            ans=(ans+S.a[0][i])%mod;
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    D题

    首先显然的贪心就是尽量选的远是更优的,因为能选就选显然不劣。

    一般来说是一个个加边试探过去,看看能否成立。这里就可以用倍增优化

    直到最后一段区间没法用倍增,因为会超过K,这段区间使用二分check一下。

    在这样的优化下,tarjan算法的复杂度就能够满足条件了

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e6+10;
    const int mod=1e9+7;
    int n,m;
    ll k,sum;
    int x[N],y[N];
    vector<int> g[N];
    int dfn[N],low[N];
    int ins[N],idx;
    stack<int> q;
    void tarjan(int u){
        dfn[u]=low[u]=++idx;
        int i;
        q.push(u),ins[u]=1;
        for(i=0;i<g[u].size();i++){
            int j=g[u][i];
            if(!dfn[j]){
                tarjan(j);
                low[u]=min(low[u],low[j]);
            }
            else if(ins[j]){
               low[u]=min(low[u],dfn[j]);
            }
        }
        if(dfn[u]==low[u]){
            ll tmp=0;
            while(1){
                int t=q.top();
                q.pop();
                ins[t]=0;
                tmp++;
                if(t==u)
                break;
            }
            sum+=(tmp*tmp);
        }
    }
    ll check(int l,int r){
        int i,j;
        r=min(r,m);
        vector<int> num;
        for(i=l;i<=r;i++){
            g[x[i]].push_back(y[i]);
            num.push_back(x[i]);
            dfn[x[i]]=dfn[y[i]]=0;
        }
        sum=n;
        for(i=0;i<num.size();i++){
            if(!dfn[num[i]]){
                idx=0;
                tarjan(num[i]);
                sum-=idx;
            }
        }
        for(i=l;i<=r;i++){
            g[x[i]].clear();
        }
        return sum;
    }
    int main(){
        ios::sync_with_stdio(false);
        cin>>n>>m>>k;
        int i;
        for(i=1;i<=m;i++){
            cin>>x[i]>>y[i];
        }
        int now=1;
        int ans=0;
        for(now=1;now<=m;now++){
            int i=0;
            while(check(now,now+(1<<i)-1)<=k){
                if(now+(1<<i)-1>m)
                    break;
                i++;
            }
            int l=now+(1<<(i-1))-1,r=min(m,now+(1<<i)-1);
            while(l<r){
                int mid=l+r+1>>1;
                if(check(now,mid)<=k){
                    l=mid;
                }
                else{
                    r=mid-1;
                }
            }
            now=l;
            ans++;
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    E题

    首先看看官方题解:

    -----------------------------------------------

    考虑先把所有询问离线,然后从小到大枚举询问右端点,用某种数据结构维护每个左端点的答案。

    考虑对于一个点,只保留最后一次覆盖它的线段,并把这个点的权值记为线段的标号。

    考虑把相邻的权值相同的点合并成同一段来维护,段的权值就是这些点的权值。那么新加入一条线段,就相当于合并(或者说删除)它覆盖到的旧的段,把这些段的权值变成当前枚举到的右端点。

    当然可能有一些段会被当前线段从中截断,这种段至多有两个,可以先把它们分解成两段。于是我们可以假定新加入的线段覆盖到的每个段都是完整的。

    对应在统计答案的数据结构上,如果合并了一个权值是 xx 的段 AA,相当于数据结构上 [x+1,r][x+1,r] 这个区间的答案整体加上段 AA 的长度,其中 rr 是当前的右端点。

    由于询问是单点询问,因此可以用树状数组来维护差分数组实现区间修改(或者直接使用线段树)。

    考虑如何维护出覆盖了哪些线段。可以使用 set 来维护每两个连续段之间的分界点,每次暴力合并连续段即可。根据均摊理论,总复杂度就是O(nlogn)。

    --------------------------------------------------

    解释就是因为我们从小枚举右端点,因此树状数组就能够维护的是i到当前r的最大覆盖范围。

    我们用set维护所有线段,并且每条线段都用最大的位置来取代他,这样就能够在树状数组上就可以刚好维护而不会重复维护。

    具体注释可以看代码。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e5+10;
    const int mod=1e9+7;
    ll tr[N];
    ll ans[N];
    int lowbit(int x){
        return x&-x;
    }
    void add(int x,int c){
        int i;
        for(i=x;i<N;i+=lowbit(i)){
            tr[i]+=c;
        }
    }
    ll sum(int x){
        int i;
        ll res=0;
        for(i=x;i;i-=lowbit(i)){
            res+=tr[i];
        }
        return res;
    }
    set<pll> s;
    int L[N],R[N];
    vector<pll> num[N];
    int main(){
        ios::sync_with_stdio(false);
        int i;
        int n,q;
        cin>>n>>q;
        for(i=1;i<=n;i++){
            cin>>L[i]>>R[i];
        }
        for(i=1;i<=q;i++){
            int a,b;
            cin>>a>>b;
            num[b].push_back({a,i});
        }
        s.insert({1e9+10,0});//m1存贮的是线段的两两分界点
        for(i=1;i<=n;i++){
            int l=L[i],r=R[i];
            int pos=-1,d=l-1;
            auto it=s.lower_bound({l-1,0});//找到大于l-1的第一个点
            while(1){
                int x=it->first,id=it->second;//找到位置和id
                int tmp=min(x,r)-d;//计算长度
                add(id+1,tmp);//因为到id的位置这段都已经覆盖过了,所以用了i后,id+1到i需要加上这段长度
                add(i+1,-tmp);
                if(pos==-1){
                    pos=id;//记录分界点
                }
                if(x<=r){
                    auto it1=it;
                    it++;
                    d=x;
                    s.erase(it1);
                }
                else{
                    break;
                }
            }
            if(d>1){
                s.insert({l-1,pos});//l-1的还是由碰到的第一个所控制的
            }
            s.insert({r,i});//到l-r是由i控制的
            for(int j=0;j<num[i].size();j++){
                ans[num[i][j].second]=sum(num[i][j].first);
            }
        }
        for(i=1;i<=q;i++){
            cout<<ans[i]<<endl;
        }
        return 0;
    }
    View Code
    没有人不辛苦,只有人不喊疼
  • 相关阅读:
    编译原理-词法分析01-扫描程序
    编译原理-概论-02
    编译原理-概论-01
    web api :Action Results in Web API 2
    git 学习笔记7--branch
    git 学习笔记6--remote & log
    杭电acm 1230 火星a+b(进制)
    杭电acm 1214 圆桌会议(冒泡排序)
    杭电acm 1210 Eddy's 洗牌问题
    杭电acm 1207 汉诺塔II
  • 原文地址:https://www.cnblogs.com/ctyakwf/p/14650347.html
Copyright © 2011-2022 走看看