4.11训练赛

目录

Match or Not

Divide by 2 or 3

 求和

[蓝桥杯 2018 国 B] 调手表

 [NOIP2008 普及组] 排座椅

[IOI2000] 回文字串

 口袋的天空

尴尬的数字​编辑


Match or Not

题意:给定两个字符串,问,对于s字符串的x个前缀和len(t)-x个后缀组成的新字符串s‘是否和字符串t相同。

思路:我们可以去检索所以不符合要求的下标,用set进行维护,如果set为空,则该字符串合法,否则输出no,再者,因为x是递增的,所以我们只检索对应的下标即可,不用全部重新遍历,(因为如果当前位置不合法(不匹配),接下来的下标,无论是否合法,对于整个字符串而言,都是不匹配的) 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<int,int > pll;
int mod=1e9+7;


void solve() 
{
    string s,t;
    cin>>s>>t;
    int n=s.size(),m=t.size();
    set<int> z;
    int j=0;
    for(int i=n-m;i<n;i++,j++){
        if(s[i]!='?'&&t[j]!='?'&&s[i]!=t[j]){
            z.insert(j);
        }
    }
    if(z.empty()){
        cout<<"Yes"<<endl;
    }
    else{
        cout<<"No"<<endl;
    }
    for(int i=0;i<m;i++){
        z.erase(i);
        if(s[i]!='?'&&t[i]!='?'&&s[i]!=t[i]){
            z.insert(j);
        }
        if(z.empty()){
            cout<<"Yes"<<endl;
        }
        else{
            cout<<"No"<<endl;
        }
    }
}


int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    //system("pause");
    return 0;
}

Divide by 2 or 3

题意:给定指定长度的数组,对于数组中的数字,如果能被2或3整除,可以将该数除以2或3(当然也可以不操作)问,经过有限次操作后,是否能使数组中所有数字相同。

思路:一开始的想法是,寻找最小值,然后看每个数是否能通过除以2或3变成最小值,但后来发现,存在一种情况,即有的数不能变到最小值,但最后整个数组依然合法。(比如3和16,我们可以通过操作将其全部变为1 1)。所以接下来就考虑求出整个数组的gcd,将上文所述的最小值替换为gcd即可。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
typedef long long ll;
typedef pair<int,int> pll;
int mod=1e9+7;

void solve()
{
    int n;
    cin>>n;
    vector<int> a(n);
    ll cnt=0;
    int gcd=0;
    for(int i=0;i<n;i++){
        cin>>a[i];
        gcd=__gcd(gcd,a[i]);
    }
    for(int i=0;i<n;i++){
        if(a[i]%gcd!=0){
            cout<<-1<<endl;
            return ;
        }
        else{
            int s=0;
            int x=a[i]/gcd;
            while(x%3==0){
                x/=3;
                s++;
            }
            while(x%2==0){
                x/=2;
                s++;
            }
            if(x>1){
                cout<<-1<<endl;
                return;
            }
            else{
                cnt+=s;
            }
        }
    }
    cout<<cnt<<endl;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t=1;
    while(t--){
        solve();
    }
    return 0;
}

 求和

题意:由于是中文,就不再赘述了。

思路:对题目进行浏览后,题目要我们所求的式子,我们可以发现,跟题目所给出的y没有关系。那么,对第一个条件进行翻译,即为,选择两个位置x,z,让y为x,z的平均数且为整数,即再通俗易懂一点就是,保证x+z的值一定是偶数。

再进行第二个条件的翻译,保证x和z方块上的颜色相等。

题目给出的number数组其实在整个题目中,只在最后求答案的时候起了作用,所以不要被题目花里胡哨的数据给吓到。别看有下标,颜色,编号,看起来很复杂,其实只是考察的推式子(应该存在其他做法,若觉得笔者复杂可以自行搜素)

如上文所说,我们需要保证x和z的颜色相同,且x+z的和为偶数,那么我们就可以将相同颜色的下标用map进行维护,然后去对每个颜色所存的下标进行遍历,最后进行累加。但对于n个相同的颜色,无论是否合法(即相加为偶数)的话,遍历的 的时间复杂度为n*n,对于此题的数据会tle。所以我们要想方法进行优化,因为要去检查是否合法,我们就想,是否可以将下标进行分类,然后运用一些式子,把答案计算出来。我们可以发现,下标非奇即偶,因此我们可以把下标的为奇数的存一个数组,偶数的存一个数组,这样就解决了对于合法的检验。那么剩下来的就只剩如何快速求出答案。我们可以尝试一下,比如,偶数数组中有三个数,那么偶数数组的答案总和为:(a_{1}+a_{2})*(num_{1}+num_{2})+(a_{2}+a_{3})*(num_{2}+num_{3})+(a_{1}+a_{3})*(num_{1}+num_{3})

我们对其进行化简,可以得到(2*a1*num1+a1*num2+a1*num3)+(2*a2*num2+a2*num1+a2*num3)+(2*a3*num3+a3*num1+a3*num2),结构相同,我们对于每一个下标,单独拿出看,为a1*num1+a1*(num1+num2+num3)。

这里直接给出结论,对于长度为n的公式,单独拆开来看,其组成为:

(n-2)*a_{n}*num_{n}+a_{n}*(num_{1}+num_{2}+......+num_{n})

由此我们可以预处理出前n个num的和,然后就可以成功将n*n的时间复杂度降为n的时间复杂度。

最后注意取模就好。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
typedef long long ll;
typedef pair<int,int> pll;
int mod=10000+7;
ll num[N],co[N];
void solve()
{
    int n,m;
    cin>>n>>m;
    ll ans=0;
    map<ll,vector<ll> > mp;
    for(int i=1;i<=n;i++){
        cin>>num[i];
    }
    for(int i=1;i<=n;i++){
        cin>>co[i];
        mp[co[i]].push_back(i);
    }
    for(auto x: mp){
        auto v=x.second;
        vector<int> a,b;
        ll sum1=0,sum2=0;
        for(int i=0;i<v.size();i++){
            if(v[i]%2==0){
                a.push_back(v[i]);
                sum1+=num[v[i]];
            }
            else{
                b.push_back(v[i]);
                sum2+=num[v[i]];
            }
        }
        for(auto x: a){
            ans=(ans%mod+((a.size()-2)*x*num[x]+x*sum1)%mod)%mod;
        }
        for(auto x: b){
            ans=(ans%mod+((b.size()-2)*x*num[x]+x*sum2)%mod)%mod;
        }

    }
    cout<<ans<<endl;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    //system("pause");
    return 0;
}

[蓝桥杯 2018 国 B] 调手表

思路:BFS的应用。我们可以将起点看作0,然后把终点看作1-n中的所有数,最后所求即为到打个点最小值的最大值。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<int,int > pll;
int mod=1e9+7;
bool st[N];
int t[N];
void solve() 
{
    int n,k;
    cin>>n>>k;
    queue<int> q;
    q.push(0);
    st[0]=1;
    int ans=0;
    while(!q.empty()){
        int fr=q.front();
        //cout<<t[fr]<<endl;
        q.pop();
        ans=max(ans,t[fr]);
        int x=(fr+k)%n,y=(fr+1)%n;
        if(!st[x]){
            t[x]=t[fr]+1;
            st[x]=1;
            q.push(x);
        }
        if(!st[y]){
            t[y]=t[fr]+1;
            st[y]=1;
            q.push(y);
        }
    }
    cout<<ans<<endl;
}


int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    system("pause");
    return 0;
}

 [NOIP2008 普及组] 排座椅

 

思路:开两个结构体,去维护行和列的信息,一开始所有的价值为0,对于题目给定的两个坐标,若横坐标不同,即为行不同,min(x1,x2)所对应的行的价值++,对于列同理。最后对价值进行排序,对于行和列输出对应的个数即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<int,int > pll;
int mod=1e9+7;
struct node
{
    int v,l,r;
}h[N],li[N];

void solve() 
{
    int m,n,k,l,d;
    cin>>m>>n>>k>>l>>d;
    for(int i=1;i<m;i++){
        h[i].l=i;
        h[i].r=i+1;
    }
    for(int i=1;i<n;i++){
        li[i].l=i;
        li[i].r=i+1;
    }
    for(int i=0;i<d;i++){
        int x1,y1,x2,y2;
        cin>>x1>>y1>>x2>>y2;
        if(x1!=x2){
            h[min(x1,x2)].v++;
        }
        else{
            li[min(y1,y2)].v++;
        }
    }
    sort(h,h+m,[](node a,node b){return a.v>b.v;});
    sort(h,h+k,[](node a,node b){return a.l<b.l;});
    sort(li,li+n,[](node a,node b){return a.v>b.v;});
    sort(li,li+l,[](node a,node b){return a.l<b.l;});
    for(int i=0;i<k;i++){
        cout<<h[i].l<<" ";
    }
    cout<<endl;
    for(int i=0;i<l;i++){
        cout<<li[i].l<<" ";
    }
    cout<<endl;
}


int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    //system("pause");
    return 0;
}

[IOI2000] 回文字串

思路:将字符串翻转然后对于两个字符串,求出LCS,用n减去LCS的值即可。

#include<bits/stdc++.h>
using namespace std;
int dp[1005][1005];
int main()
{
    string a;
    cin>>a;
    int n=a.size();
    string b=a;
    reverse(a.begin(),a.end());
    a=' '+a;
    b=' '+b;
    for(int i=1;i<a.size();i++){
        for(int j=1;j<b.size();j++){
            if(a[i]!=b[j]){
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
            else{
                dp[i][j]=dp[i-1][j-1]+1;
            }
        }
    }
    cout<<n-dp[n][n]<<endl;
    system("pause");
    return 0;
}

 口袋的天空

思路:

有一句话说的是 如果n个点被n-1条边连接的话,这一定是棵树。
那么:
连的边数 得到的树的个数

n-1 1

n-2 2

n-3 3

... ...

n-k k

所以我们如果想要连出k棵树,就需要连n-k条边。题目要求用n朵云连出k个棉花糖。因为每个棉花糖都是连通的,那么每个棉花糖就相当于是一棵树。就是说要用n个节点连出k棵树。
也就是说要用n-k条边连出k棵树。也就是说要花费连出n-k条边的代价。

简言之就是,构造出k个节点的最小生成树。

//kruskal求最小生成树,适用于稀疏图,时间复杂度mlogm
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;

struct node
{
    int a,b,c;
}e[N];
int n,m,k;
int p[N];
int find(int x)
{   
    if(p[x]!=x){
        return p[x]=find(p[x]);
    }
    return x;
}
void krustal()
{
    int res=0;
    int cnt=n;
    for(int i=0;i<m;i++){
        if(cnt==k){
            cout<<res<<endl;
            return ;
        }
        int a=e[i].a,b=e[i].b,c=e[i].c;
        int pa=find(a),pb=find(b);
        if(pa!=pb){
            res+=c;
            cnt--;
            p[pa]=pb;
        }
    }
    if(cnt!=k){
        cout<<"No answer"<<endl;
    }
    else{
        cout<<res<<endl;
    }

}
bool cmp(node x,node y)
{
    return x.c<y.c;
}
int main()
{
    cin>>n>>m>>k;
    int i;
    for(i=0;i<m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        e[i]={a,b,c};
    }
    for(i=0;i<=n;i++){
        p[i]=i;
    }
    sort(e,e+m,cmp);
    krustal();
    system("pause");
    return 0;
}

尴尬的数字

题意: 给定一个数的二进制和三进制表示,二进制表示和三进制表示中,有一位是错误的,要你输出该的正确值。

思路:题目所说,n可能到1e9,就算到1e18都没有关系,因为二进制表示只有那么多位数,所有我们对每一位二进制和三进制表示数的每一位进行修改,然后记录值,用map进行维护,最后value值为2的即为答案。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
typedef pair<int,int > pll;
int mod=1e9+7;


ll get2(vector<int> x)
{   
    int n=x.size()-1;
    ll res=0;
    for(int i=0;i<x.size();i++){
        res+=x[i]*pow(2,n);
        n--;
    }
    return res;
}
ll get3(vector<int> x)
{
    int n=x.size()-1;
    ll res=0;
    for(int i=0;i<x.size();i++){
        res+=x[i]*pow(3,n);
        n--;
    }
    return res;
}
void solve() 
{
    string a,b;
    cin>>a>>b;
    vector<int> n2,n3;
    for(int i=0;i<a.size();i++){
        n2.push_back(a[i]-'0');
    }
    for(int i=0;i<b.size();i++){
        n3.push_back(b[i]-'0');
    }
    map<ll,int> z;
    for(int i=0;i<n2.size();i++){
            ll res=0;
            if(n2[i]==0){
                n2[i]=1;
                res=get2(n2);
                n2[i]=0;
            }
            else{
                n2[i]=0;
                res=get2(n2);
                n2[i]=1;
            }
            z[res]++;
    }
    for(int i=0;i<n3.size();i++){
            ll res=0;
            if(n3[i]==0){
                n3[i]=1;
                res=get3(n3);
                z[res]++;
                n3[i]=2;
                res=get3(n3);
                z[res]++;
                n3[i]=0;
            }
            else if(n3[i]==1){
                n3[i]=0;
                z[get3(n3)]++;
                n3[i]=2;
                z[get3(n3)]++;
                n3[i]=1;
            }
            else{
                n3[i]=1;
                z[get3(n3)]++;
                n3[i]=0;
                z[get3(n3)]++;
                n3[i]=2;
            }
    }
    for(auto x: z){
        if(x.second==2){
            cout<<x.first<<endl;
            return ;
        }
    }
}


int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    //system("pause");
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值