Codeforces Global Round 25(A-E)

本文介绍了四个编程题目,涉及逻辑判断、优化策略和字符串处理。A题考察奇偶性判断,B题涉及比赛胜场策略,C题讨论最优购票方案,D题解决珠宝购买问题,E题探讨非回文串构造。
摘要由CSDN通过智能技术生成

A. Dual Trigger

        这道题只能将灯泡从0变为1,所以我们观察最终状态s的1的位置,可以看出:

        1.1的个数为奇数不合法,输出NO;

        2.1的个数为2且相连,输出NO;

        3.其他情况均为YES。

void solve()
{
    string s; int n,i;
    cin>>n>>s; s="."+s;
    vector<int>vec;//将所有为1的下标存储方便特判
    for(i=1;i<=n;i++)
        if(s[i]=='1') vec.push_back(i); 
    if(vec.size()%2) cout<<"NO\n";
    else
    {
        if(vec.size()==2) 
        {
            if(vec[0]==vec[1]-1) cout<<"NO\n";
            else cout<<"YES\n";
        }
        else cout<<"YES\n";
    }
}

B. Battle Cows

 

         其实就是要每场比赛都要下一个人与上一场的获胜者进行较量,如果第k个人前面有比他大的,则当前方案胜场为0。所以我们要让第k个人尽量靠前或将比k大的第一个人向后移,即求出第k个人和第一个人交换第k个人和第一个比它大的人交换的方案取更优的即可。

int a[N];
void solve()
{
    int n,k,i; cin>>n>>k;
    for(i=1;i<=n;i++) cin>>a[i];
    swap(a[k],a[1]);
    int ans1=0;  //与第一个人交换的方案的胜场数
    for(i=2;i<=n;i++)
    {
        if(a[i]>a[1]) break;
        else ans1++;
    }
    swap(a[k],a[1]); //swap回来进行下一个方案的求解

    int flag=n+1,ans2=0; //flag为第一个比k大的人的下标
    for(i=1;i<=n;i++)
        if(a[i]>a[k])
        {
            flag=i;
            break;
        }
    if(flag<k)
    {
        swap(a[k],a[flag]);
        if(flag>1) ans2++;//特判当前k是不是在开头,否则会赢前面的人一场
        for(i=flag+1;i<=n;i++)
        {
            if(a[i]>a[flag]) break;
            else ans2++;
        }
    }
    cout<<max(ans1,ans2)<<"\n";
}

C. Ticket Hoarding

 

         该题可以看出,若是我们确保要买一些天的票,我们会尽可能的购买至上限而不是这些天平均分配(会导致买到价格提高的票更多),所以对于买k张门票,我们会有一个这样的购买数量序列{m,m...m,m,x(x=k%m)}(加和为k)(可以交换位置),我们可以枚举这些天哪天购买了x个即可。

        对于怎样确定这些天,我们可以贪心的将最小的前几天选出来进行操作。

#define int long long
#define PII pair<int,int>
#define fir first
#define sec second
const int inff=0x3f3f3f3f3f3f3f3f
PII a[N]; //一维存价格,二维存下标
bool cmp(PII x,PII y) {return x.sec<y.sec;}
void solve()
{
    int n,m,k,i; cin>>n>>m>>k;
//注意要用long long,或者宏定义int
    for(i=1;i<=n;i++)
    {
        int x,y=i; cin>>x;
        a[i]={x,y};
    }
    sort(a+1,a+1+n);  //按照大小排序
    int d=(m+k-1)/m,x=k-(d-1)*m; //d为需要枚举的天数
    sort(a+1,a+1+d,cmp); //前d个必选,按照下标排序
    int sum=0,ans=inff;
    for(i=1;i<=d;i++) sum+=(a[i].fir+(i-1)*m)*m; //每个都选m个,接下来枚举哪个是x即可
    for(i=1;i<=d;i++)
    {
        int pr=(i-1)*m,nex=(d-i)*m;
        //pr为前面购买的个数,nex为后续购买的个数
        int cnt=sum-(a[i].fir+pr)*m+(a[i].fir+pr)*x;//当前这位从购买m个变为x个修改的价格
        int sub=m-x; //后续购买每个物品降低的价格
        cnt-=sub*nex;
        ans=min(ans,cnt);
    }
    cout<<ans<<"\n";
}

        需要注意的是,其实我们不去枚举哪个是x也可以,排序后的选择并不影响最终结果,可以直接按照价格从小到大贪心的买到上限即可,以下是证明过程:

 D. Buying Jewels

         这道题有以下两种显而易见的情况:

        1.k>n,无解

        2.n%k=0,输出一个价格n/k即可

        在其他情况,我们可以尝试构造出买了一个珠宝后剩下的余数恰好可以购买一元一个的珠宝,所以我们令下一步的操作使k-1且n=k,若能构成这一步则输出两堆答案即可,否则可以证明无解(购买个数不足),证明过程如下:

       

void solve()
{
    int n,k; cin>>n>>k;
    if(k>n) { cout<<"NO\n"; return ; }
    if(n%k==0) { cout<<"YES\n1\n"<<n/k<<"\n"; return ; }
    int next=k-1; //令下一步n和k都为next
    if(n/(n-next)>1) { cout<<"NO\n"; return ; }
    //该情况构造不出n=k=next的情况
    cout<<"YES\n2\n"<<n-next<<" "<<"1\n";
}

E. No Palindromes

        第二行的重码是回文串的意思。

        赛场上捏了几个样例,发现三个非回文串拼接可以构造成两个非回文串。

        例如:{ab,ba,ab}可以变为{abb,aab}且我没有找到反例,所以我认为最终答案若有解只可能是一整段是非回文串或分成两段非回文串。

        题解证明过程如下:

         所以只需要特判整个是不是回文串,并且枚举断点,正反跑两遍字符串哈希对比一下即可。

typedef unsigned long long ULL;
 
const int P = 131;
 
int n, m;
char s[N],t[N];
ULL h1[N], p1[N];
ULL h2[N], p2[N];
 
void init1()
{
    p1[0] = 1;
    for (int i = 1; i <= n; i ++ )
    {
        h1[i] = h1[i - 1] * P + s[i];
        p1[i] = p1[i - 1] * P;
    }
}
void init2()
{
    p2[0] = 1;
    for (int i = 1; i <= n; i ++ )
    {
        h2[i] = h2[i - 1] * P + t[i];
        p2[i] = p2[i - 1] * P;
    }
}
ULL get1(int l, int r)
{
    return h1[r] - h1[l - 1] * p1[r - l + 1];
}
ULL get2(int l, int r)
{
    return h2[r] - h2[l - 1] * p2[r - l + 1];
}
void solve()
{
    cin>>s+1; int i; n=strlen(s+1);
    for(i=1;i<=n;i++) t[i]=s[n-i+1];
    init1(); init2();
    if(get1(1,n)!=get2(1,n))
    {
        cout<<"YES\n1\n";
        cout<<s+1<<"\n";
        return ;
    }
    for(i=2;i<n-1;i++)
    {
        int l1=1,r1=i;
        int l2=i+1,r2=n;
        if(get1(l1,r1)!=get2(n-i+1,n)&&get1(l2,r2)!=get2(1ll,n-i))
        {
            cout<<"YES\n2\n";
            for(int j=1;j<=i;j++) cout<<s[j]; cout<<" ";
            for(int j=i+1;j<=n;j++) cout<<s[j]; cout<<"\n";
            return ;
        }
    }
    cout<<"NO\n";
}

  • 33
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值