牛客练习赛 67

A.牛牛爱字符串

注意原字符串有空格,不要用cin

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    
    
    IO;
    int T=1;
    //cin>>T;
    while(T--)
    {
    
    
        string s;
        while(getline(cin,s))
        {
    
    
            vector<string> ans;
            int n=s.size();
            for(int i=0;i<n;i++)
            {
    
    
                
                if(s[i]<'0'||s[i]>'9') continue;
                string now="";
                while(i<n&&s[i]>='0'&&s[i]<='9')
                {
    
    
                    now+=s[i];
                    i++;
                }
                reverse(now.begin(),now.end());
                while(now.size()>1&&now.back()=='0') now.pop_back();
                reverse(now.begin(),now.end());
                ans.push_back(now);
            }      
            for(auto t:ans) cout<<t<<' ';
            cout<<'\n';
        }
    }
    return 0;
}

B.牛牛爱位运算

&只会越运算越小,直接选择最大的数即可

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
int a[N],n;
int main()
{
    
    
    IO;
    int T=1;
    cin>>T;
    while(T--)
    {
    
    
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        sort(a+1,a+1+n);
        cout<<a[n]<<'\n';
    }
    return 0;
}

C.牛牛爱博弈

打表找规律,懂得都懂
最终只要 n n n不是 3 3 3的倍数先手必胜

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_set>
using namespace std;
const int N=110;
int sg[N];
// int dfs(int u)
// {
    
    
//     if(sg[u]!=-1) return sg[u];
//     unordered_set<int> mp;
//     for(int i=0;(1<<i)<=u;i++) mp.insert(dfs(u-(1<<i)));
    
//     for(int i=0;;i++)
//         if(!mp.count(i)) return sg[u]=i;
// }
int main()
{
    
    
    IO;
    int T=1;
    cin>>T;
    memset(sg,-1,sizeof sg);
    while(T--)
    {
    
    
        cin>>n;
        if(n%3) cout<<"Alan\n";
        else cout<<"Frame\n";
        
    }
    // for(int i=0;i<=100;i++)
    // {
    
    
    //     cout<<i<<' ';
    //     if(dfs(i)) cout<<"Alan\n";
    //     else cout<<"Frame\n";
    // }
    return 0;
}

D. 牛妹爱数列

f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示:考虑前 i i i个序列,把他们全变成 0 / 1 0/1 0/1的最小翻转次数
转移:考虑最后一步是单点修改还是前缀修改。
如果是单点修改 f [ i ] [ 1 ] = m i n ( f [ i ] [ 1 ] , f [ i − 1 ] [ 1 ] + 1 − a [ i ] ) , f [ i ] [ 0 ] = m i n ( f [ i ] [ 0 ] , f [ i − 1 ] [ 0 ] + a [ i ] ) f[i][1]=min(f[i][1],f[i-1][1]+1-a[i]),f[i][0]=min(f[i][0],f[i-1][0]+a[i]) f[i][1]=min(f[i][1],f[i1][1]+1a[i]),f[i][0]=min(f[i][0],f[i1][0]+a[i])
如果是前缀修改 f [ i ] [ 1 ] = m i n ( f [ i ] [ 1 ] , f [ i − 1 ] [ 0 ] + a [ i ] + 1 ) , f [ i ] [ 0 ] = m i n ( f [ i ] [ 0 ] , f [ i − 1 ] [ 1 ] + 1 − a [ i ] + 1 ) f[i][1]=min(f[i][1],f[i-1][0]+a[i]+1),f[i][0]=min(f[i][0],f[i-1][1]+1-a[i]+1) f[i][1]=min(f[i][1],f[i1][0]+a[i]+1),f[i][0]=min(f[i][0],f[i1][1]+1a[i]+1)

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
int f[N][2];
int a[N],n;
int main()
{
    
    
    IO;
    int T=1;
    //cin>>T;
    while(T--)
    {
    
    
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        memset(f,0x3f,sizeof f);
        f[0][0]=f[0][1]=0;
        for(int i=1;i<=n;i++)
        {
    
    
            f[i][0]=min(f[i-1][0]+a[i],f[i-1][1]+1-a[i]+1);
            f[i][1]=min(f[i-1][0]+1-a[i]+1,f[i-1][1]+1-a[i]);
        }
        cout<<f[n][0]<<'\n';
    }
    
    return 0;
}

E.牛妹游历城市

暴力建图边数达到恐怖的 n 2 n^2 n2不光空间开不下,而且时间跑不过,不可取。
考虑优化建图:建立 32 32 32个虚拟源点(编号 n + 1 … n + 32 ) n+1 \dots n+32) n+1n+32)如果一个点 i i i a i a_i ai的二进制表示中第 j ( 0 ≤ j ≤ 31 ) j(0\leq j\leq 31) j(0j31)位是1,那么连一条边 i − > n + j + 1 i->n+j+1 i>n+j+1权值是 1 < < j 1<<j 1<<j的边,并且连一条 n + j + 1 − > i n+j+1->i n+j+1>i权值是 0 0 0的边。然后不难发现原图中的所有边都可以转化成新图的边,同样新图的所有边都能转化成原图的边。 但是并不影响求最短路比如权值是101011101010的两个点,我们会建立边权是10、1000、100000这些路径,但是发现这些相当于重边,不会影响答案。
博主的图非常清楚直接秒懂

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pli;
const int N=111010,M=1000010;
const ll INF=0x3f3f3f3f3f3f3f3f;
int h[N],e[M],ne[M],idx;
ll w[M],a[N];
ll dist[N];
bool st[N];
int n;
void add(int a,int b,ll c)
{
    
    
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
void dijkstra()
{
    
    
    memset(dist,0x3f,sizeof dist);
    memset(st,0,sizeof st);
    dist[1]=0;
    priority_queue<pli,vector<pli>,greater<pli>> q;
    q.push({
    
    0,1});
    while(q.size())
    {
    
    
        int t=q.top().second;q.pop();
        if(st[t]) continue;
        st[t]=1;
        for(int i=h[t];i!=-1;i=ne[i])
        {
    
    
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
    
    
                dist[j]=dist[t]+w[i];
                q.push({
    
    dist[j],j});
            }
        }
    }
}
int main()
{
    
    
    IO;
    int T=1;
    cin>>T;
    while(T--)
    {
    
    
        cin>>n;
        memset(h,-1,sizeof h);
        idx=0;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++)
            for(int j=0;j<32;j++)
                if(a[i]>>j&1)
                {
    
    
                    add(i,j+n+1,1ll<<j);
                    add(j+n+1,i,0ll);
                }
        dijkstra();
        if(dist[n]==INF) cout<<"Impossible\n";
        else cout<<dist[n]<<'\n';
    }
    return 0;
}

学习ST表,F明天补了

F.牛妹的苹果树

方法一:线段树(动态)+欧拉序ST表求lca
首先有一个引理:树的直径具有可合并的性质,同一棵树上两个区间的直径的两个端点分别为 ( a , b ) (a,b) (a,b) ( c , d ) (c,d) (c,d),那么合并两个区间后的新的直径的端点一定在 a , b , c , d {a,b,c,d} a,b,c,d中,通过枚举端点计算它们的距离(6种情况),取最大值可以得到两个区间合并的直径。 其正确性证明和两遍dfs求树的直径的证明过程类似。
由此我们考虑线段树维护区间直径即可。求lca最好用欧拉序+ST表的,这个我不会可以借鉴大佬写法线段树+ST表求lca
方法二:ST表维护直径(静态)+ST表求lca
由于本题为区间静态问题,并且直径有区间可重复计算的性质,考虑用ST表维护直径ST表维护直径

// O(nlogn)
#include<iostream>
#include<algorithm>
#include<cstring>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=300010,M=2*N;
int h[N],e[M],ne[M],idx;
ll w[M];
ll dist[N];
int dfn[N],eular[M],ST[M][20],cnt;
int lg[M];
pii f[N][20];
int n,q;
void add(int a,int b,ll c)
{
    
    
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}

void dfs(int u,int fa)
{
    
    
    dfn[u]=++cnt;
    ST[cnt][0]=u;
    f[u][0]={
    
    u,u};
    for(int i=h[u];i!=-1;i=ne[i])
    {
    
    
        int j=e[i];
        if(j==fa) continue;
        dist[j]=dist[u]+w[i];
        dfs(j,u);
        ST[++cnt][0]=u;
    }
}
int lca(int a,int b)
{
    
    
    a=dfn[a],b=dfn[b];
    if(a>b) swap(a,b);
    int len=lg[b-a+1];
    return dist[ST[a][len]]<dist[ST[b-(1<<len)+1][len]]?ST[a][len]:ST[b-(1<<len)+1][len];
}
ll calc(int a,int b)
{
    
    
    int pab=lca(a,b);
    return dist[a]+dist[b]-2*dist[pab];
}
void pre()
{
    
    
    for(int i=2;i<=cnt;i++) lg[i]=lg[i>>1]+1;
    
    for(int j=1;j<=lg[cnt];j++)
        for(int i=1;i+(1<<j)-1<=cnt;i++)
            ST[i][j]=dist[ST[i][j-1]]<dist[ST[i+(1<<j-1)][j-1]]?ST[i][j-1]:ST[i+(1<<j-1)][j-1];
    for(int j=1;j<=lg[n];j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
    
    
            pii a=f[i][j-1],b=f[i+(1<<j-1)][j-1];
            f[i][j]={
    
    a.x,a.y};
            if(calc(a.x,b.x)>calc(f[i][j].x,f[i][j].y)) f[i][j]={
    
    a.x,b.x};
            if(calc(a.x,b.y)>calc(f[i][j].x,f[i][j].y)) f[i][j]={
    
    a.x,b.y};
            if(calc(a.y,b.x)>calc(f[i][j].x,f[i][j].y)) f[i][j]={
    
    a.y,b.x};
            if(calc(a.y,b.y)>calc(f[i][j].x,f[i][j].y)) f[i][j]={
    
    a.y,b.y};
            if(calc(b.x,b.y)>calc(f[i][j].x,f[i][j].y)) f[i][j]={
    
    b.x,b.y};
        }
}
ll query(int l,int r)
{
    
    
    int len=lg[r-l+1];
    pii a=f[l][len],b=f[r-(1<<len)+1][len];
    ll res=calc(a.x,a.y);
    res=max(res,calc(a.x,b.x));
    res=max(res,calc(a.x,b.y));
    res=max(res,calc(a.y,b.x));
    res=max(res,calc(a.y,b.y));
    res=max(res,calc(b.x,b.y));
    return res;
}
int main()
{
    
    
    memset(h,-1,sizeof h);
    cin>>n>>q;
    for(int i=1;i<n;i++)
    {
    
    
        int a,b;
        ll c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    dfs(1,0);
    pre();
    while(q--)
    {
    
    
        int l,r;
        cin>>l>>r;
        cout<<query(l,r)<<'\n';
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Fighting_Peter/article/details/108628084