7/28 CodeForces Div2

7/28训练赛 AC率:【6:12】
A Knigh Tournamet 357C

题意:对于在(l,r)区间内的比赛,有编号为x的战胜,则每次记录下输的人是被谁所战胜的,同时每次只有赢的人能够加入到下一场的比赛中。输出所有人是被谁打败的,最终获胜的人输出为0,

思路:最开始是用vis数组来记录每个点之前是否被访问过,然后来检查每个区间,然而这样会T掉;后来是分为了l[i]~l[i-1];r[i-1]~r[i]的区间来看的,但是没有考虑到一种情况:当i+2同时覆盖了i,i+1的部分时,那么两段的x都会发生变化,因此还需要再标记,又会回到最初的想法了;因此这个时候STL上场了,反正我是需要标记那些值之前被写过的,使用set先全部放进去,后来对于已经使用过的值进行删除。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
const int MAXN=3e5+5;
set<int> kt;
set<int>::iterator it;
set<int>::iterator p[MAXN];
int ans[MAXN]; 
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        kt.insert(i);   
    }
    while(m--)
    {
        int l,r,x;
        scanf("%d %d %d",&l,&r,&x);
        int t=0;
        for(it=kt.lower_bound(l);(*it)<=r&&it!=kt.end();it++)
        {
            if((*it)!=x)
            {
                ans[*it]=x;
                p[t++]=it;
            }

        }
        for(int i=0;i<t;i++)
        kt.erase(p[i]);
    }
    it=kt.begin();
    ans[*it]=0;
    for(int i=1;i<=n;i++)
    printf("%d ",ans[i]);
    return 0;
}

C Xenia and Hamming 357D

题意:给出两个字符串,然后给出两个倍数,使得他们的长度相等,然后问通过不断地重复排列后所得到的两个字符串的不同字母个数

思路:先从两个互质长度的字符串出发,那么他们最终一定满足每个字符都至少匹配了一次,(如果为LCM(m,n),那么所有两个字符串的字符间的匹配数为1);而对于不是互质长度的,可以通过找到循环节,在循环节内的部分始终能够保证是对应的。因此只需要求出对应的gcd,lcm,通过比较在每个循环节的部分的字符的匹配就可以了。至于如何匹配的话,因为能保证所有的都能通过一次,则用【26】的字母数组来记录每个的个数,然后用个数的乘积和即可。
然而有一个需要注意的是此题的内存限制,之前没有做过会限制内存的题目,因此不是很注意这个方面的东西!!因此这个题的数据量都很大,n,m都是10^12,字符串长度也是10^6,如果最开始开成 long long cnt[MAXN][30]的话就会480KB的内存了,然而代码限制为260之内的,可以改成int 并且二维为26的,在计算结果的时候再把Int强制转为long long 的就可以了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

const int MAXN=1e6+5;
char s1[MAXN],s2[MAXN];

typedef long long ll;
int gcd(int a,int b)
{
    if(a%b==0) return b;
    return gcd(b,a%b);
}

int cnt1[MAXN][26],cnt2[MAXN][26];
int main()
{
    ll a,b;
    scanf("%lld %lld",&a,&b);
    scanf("%s",s1);
    scanf("%s",s2);
    int len1=strlen(s1);
    int len2=strlen(s2);
    int l=gcd(len1,len2);
    ll p=(ll)(len1/l)*len2; //最小公倍数
    ll sum=0;
    memset(cnt1,0,sizeof(cnt1));
    memset(cnt2,0,sizeof(cnt2));
    for(int i=0; i<len1; i++)
    {
        cnt1[i%l][s1[i]-'a']++; //进行分组
    }
    for(int i=0; i<len2; i++)
    {
        cnt2[i%l][s2[i]-'a']++;
    }
    for(int i=0; i<l; i++)
    {
        for(int j=0; j<26; j++)
        {
            sum+=(ll)cnt1[i][j]*cnt2[i][j];  //每个循环节内部的计算值
        }
    }

    ll ans=(ll)a*len1/p*(p-sum);
    printf("%lld\n",ans);
    return 0;
}

E Dima and Hares 358B

题意:我理解错了!!!!我还以为是样例错了==,以后看到和样例不一样,一定是你读错题意了!!
兔子的愉悦值受到喂养的相邻的两个编号兔子的影响,分别为首先被喂养,第二个被喂养,最后一个被喂养。(然而我理解为相邻的两个是处于饱的或者饥饿的状态,这样在状态转移的时候就会出现问题了,没有考虑到喂养的顺序,是每间隔2的来状态转移的)

思路:因为我是按照我之前错误的理解方式,在那个基础上进行改动的,所以写的可能是有些复杂了,不是很容易理解,但是也是一个很大的教训啊!
dp[i][j][k]为到了第i个兔子,左边的状态为j,右边的状态为k, 0为没吃,1为吃饱了,那么从1开始到n的话,仅dp[i]的第二维是与i-1有关的,而dp[i-1]的第三维是与i有关的,dp[i][0][0]为我最先吃,所以左右两个都是饥饿的,那么dp[i-1][?][1]转移而来的,?为可以为任意值,因为i-2的部分根本不影响我i的部分,是从(i-1)的右端为1的吃饱状态来的;同样的dp[i][0][1]为右->我->左的顺序来吃的,那么还是我比左的先吃,dp[i-1][?][1]转移;dp[i][1][0]为左->我->右顺序,那么左比我先的;dp[i][1][1]为我最后的,还是左比我先的。
事实上这样叙述可能啰嗦了一些,但是结合了状态和顺序关系,是符合我之前的想法再进行改造的。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;

const int MAXN=3005;
int dp[MAXN][2][2];
int a[MAXN],b[MAXN],c[MAXN];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    memset(dp,0,sizeof(dp));
    dp[1][1][0]=dp[1][1][1]=0; dp[1][0][1]=b[1]; dp[1][0][0]=a[1];
    for(int i=2;i<=n;i++) //理解状态转移的重点!!
    {
        dp[i][0][0]=max(dp[i-1][0][1],dp[i-1][1][1])+a[i];
        dp[i][0][1]=max(dp[i-1][0][1],dp[i-1][1][1])+b[i];
        dp[i][1][0]=max(dp[i-1][1][0],dp[i-1][0][0])+b[i];
        dp[i][1][1]=max(dp[i-1][0][0],dp[i-1][1][0])+c[i];
    }
    int ans=max(dp[n][1][0],dp[n][0][0]);
    printf("%d\n",ans);
    return 0;
}

G Dima and Text Messages 358B
题意:给你一堆单词,在每个单词的中间包括头部和尾部添加”<3”的字符,并且在所得到的新的字符串中可以再添加其他的任何元素,询问这样的字符串是否是可以由上面的方式所得到的

思路:最开始的时候想到的是暴力拆解,就是在找到每个<和3的部分,再循环是否可以找到,但是这样明显过于麻烦了,而且中间会出现一些重复的值就难以判断了。后面看到有一个思路很好,就是先构造出题目中所要求的那个最简单 的满足条件的字符串,然后在目标串中寻找是否可以有这样的满足条件即可,按照顺序来检索就可以了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;

int main()
{
    int n;
    string str,s,x;
    cin>>n;
    s="<3";
    for(int i=0;i<n;i++)
    {
        cin>>x; s+=x; s+="<3";
    }//构造出s这样的字符串来
    int len=s.size();
    cin>>str;
    int l=str.size();
    int j=0;
    for(int i=0;i<l;i++)
    {
        if(str[i]==s[j]) j++;
        if(j>=len) //搜索是否都出现了的
        {
            cout<<"yes"<<endl;
            return 0;
        }
    }
    cout<<"no"<<endl;
    return 0;
}

H Dima and Containers 358C

题意:对于一串数,可以放进stack,queue,deck中,stack是尾进尾出,queue是尾进首出,deck是首尾均可进可出,每次最多能找到三个数,对于每一个数需要知道是如何存储的,数为0的时候表示的是输出值

思路:那么找到前三大的数就可以了。假设【l,r】中的三个大数为a,b,c,那么可以把他们分别放到三个容器里面。只是有一个大的坑点!!就是没有0的时候,需要特别考虑,这个时候可以随便放,但是个数需要能够确定下来!

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;

const int MAXN=1e5+5;
struct Node
{
    int v,id;
} a[MAXN];
int b[MAXN];
int cnt[MAXN];
bool cmp(Node m,Node n)
{
    if(m.v==n.v) return m.id<n.id;
    return m.v>n.v;
}
int main()
{
    int n;
    scanf("%d",&n);
    int tot=0;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&b[i]);
        if(b[i]==0) cnt[tot++]=i;

    }
    string in[6]={"pushStack","pushQueue","pushFront","pushBack"};
    string out[6]={"popStack","popQueue","popFront","popBack"};
    int st=0;
    for(int i=0;i<tot;i++)
    {
        int ed=cnt[i];
        if(st==ed) puts("0");
        else if(ed-st<=3)
        {
            for(int j=0;j<ed-st;j++)
                cout<<in[j]<<endl;
            printf("%d",ed-st);
            for(int j=0;j<ed-st;j++)
                cout<<" "<<out[j];
            cout<<endl;
        }
        else
        {
            memset(a,0,sizeof(a));
            for(int j=st;j<ed;j++)
                {a[j-st].v=b[j];a[j-st].id=j;}
            sort(a,a+ed-st,cmp);

            for(int j=st;j<ed;j++)
            {
                if(j==a[0].id) cout<<in[1]<<endl;
                else if(j==a[1].id) cout<<in[0]<<endl;
                else if(j==a[2].id) cout<<in[2]<<endl;
                else cout<<in[3]<<endl;
            }
            puts("3 popStack popQueue popFront");
        }
        st=cnt[i]+1;
    }
    if(tot==0) //当为0的是否特别需要考虑,因为此时tot=0,那么cnt[tot-1]实际上并不存在。
    {
        for(int i=0;i<n;i++)
        cout<<in[1]<<endl;
    }
    else
    {

    for(int i=cnt[tot-1]+1;i<n;i++)
        cout<<in[1]<<endl;
    }
    return 0;
}

I Prime Number 359C

题意:很多的数组成分子,分母,对应的分母相乘通分后的分子分母的最大公约数。

思路:还是自己想的复杂了!!这样的题目还是需要能够去转换一个思维去琢磨的。需要对于从最大的数的个数来不断模x,当满足的时候就需要能够再往下来进行寻找。因此可以采用multiset的数据结构来记录最大值跟每个值的差,对于能够进行化简的就需要能够不断增大差值。最后需要考虑到全为0的情况,如2 2 0 0结果为1,只要满足数目cnt%x==0就会有ans+1,因此就需要使用min(ans,sum)来避免这一情况

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
typedef long long ll;
multiset<ll> st;
const int MAXN=1e5+5;
ll a[MAXN];
const int mod=1e9+7;

ll pow2(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans%mod;
}
int main()
{
    int n;
    ll x;
    cin>>n>>x;
    ll sum=0;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
        sum+=a[i];
    }
    ll ans=sum-a[n];
    for(int i=1; i<=n; i++) st.insert(a[n]-a[i]);
    ll cnt=0,num=0;
    while(1)
    {
        cnt=st.count(num);
        if(cnt>0) st.erase(num);
        ll k=cnt/x;
        if(cnt%x!=0) break;
        if(k) ans++;
        num++;
        for(int i=1; i<=k; i++) st.insert(num);
        if(st.empty()) break;
    }
    //cout<<ans<<endl<<sum<<endl;
    ans=min(ans,sum);  //考虑到全为0,且有恰好能整除的时候的情况下来看的
    cout<<pow2(x,ans)<<endl;
    return 0;
}

J Pair of Numbers 359D

题意:找到连续序列中的某一个数是该序列中的所有数的因子,在有最长长度的情况下,输出有哪些可能的起始值。

思路:那么可以对于每个i,可以向两边进行扩展,左端l,右端r,记录每次的最长的长度,并更新这个长度的个数最后输出。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

const int MAXN=300005;
int a[MAXN];
int ans[MAXN];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int l,r;
    int len=0,cnt=0;
    for(int i=1;i<=n;)
    {
        l=r=i;
        while(a[l]%a[i]==0&&l>0) l--;
        while(a[r]%a[i]==0&&r<=n) r++;
        i=r;
        if(r-l-2>len){
            cnt=0; len=r-l-2;
        }
        if(r-l-2==len) ans[cnt++]=l+1;
    }
    printf("%d %d\n",cnt,len);
    for(int i=0;i<cnt;i++)
        printf("%d%c",ans[i],(i==cnt-1)?'\n':' ');
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值