4.27-4.28周训

E:Stas and the Queue at the Buffet

题目链接:https://vjudge.net/contest/297959#problem/E

题目大意:n个学生,第i个学生的初始位置为i。每个学生有两个参数a,b,第i个学生的不满意值为

ai⋅(i−1)+bi⋅(n−i)。现在我们需要对这些学生重新排列,使得所有学生的不满意值的和最小。

题解:每个学生的不满意值为:sumi=ai*(i−1)+bi*(n−i)=(ai-bi)*i-ai+n*bi

                             两边求和为:

很明显:为定值,我们要求ans的最小值,也就是要使      最小。因为i是逐渐增加的,我们要使乘积和最小,也就是要使  从大到小排列即可。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define P(x) x>0?x:0
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
const int maxn=100005;

ll n;
ll ans;
struct node
{
    ll a,b,sum;
    node(ll x=0,ll y=0,ll z=0):a(x),b(y),sum(z){}
}aa[maxn];

bool cmp(node n1,node n2)
{
    return (n1.a-n1.b)>(n2.a-n2.b);
}

int main()
{
    while(~scanf("%lld",&n))
    {
        ans=0;
        for(ll i=1;i<=n;i++)
        {
            scanf("%lld%lld",&aa[i].a,&aa[i].b);
        }
        sort(aa+1,aa+n+1,cmp);
        for(ll i=1;i<=n;i++)
        {
            aa[i].sum=(aa[i].a*(i-1)+aa[i].b*(n-i));
            ans+=aa[i].sum;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

B:Median String

题目链接:https://vjudge.net/contest/297959#problem/B

题目大意:k长度的两个字符串s和t(保证s的字典序小于t)。字典序在两个字符串中间的字符串中(保证数量是奇数),最中间的那个字符串是什么,输出。

题解:将输入的字符串看成26进制的一个大数,模拟大数相加,相除,得到最终的结果。

代码:

扫描二维码关注公众号,回复: 6731976 查看本文章
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define P(x) x>0?x:0
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
const int maxn=200005;

char s[maxn],t[maxn],sum[maxn],ans[maxn];
int k;

void median()
{
    //add
    for(int i=k-1;i>=0;i--)
    {
        sum[k-1-i]=s[i]-'a'+t[i]-'a';
    }
    for(int i=0;i<=k;i++)
    {
        if(sum[i]>25)
        {
            int quo=sum[i]/26;
            sum[i]-=26*quo;
            sum[i+1]+=quo;
        }
    }
    //divide ans/2
    for(int i=k;i>=0;i--)//sum中高位在后,低位在前
    {
        int mod=sum[i]%2;
        ans[i]=sum[i]/2;
        if(mod)
            sum[i-1]+=mod*26;
    }
    for(int i=k-1;i>=0;i--)
    {
        ans[i]+='a';
    }
}

int main()
{
    while(~scanf("%d",&k))
    {
        getchar();
        scanf("%s",s);
        scanf("%s",t);
        memset(ans,0, sizeof(ans));
        median();
        for(int i=k-1;i>=0;i--)//ans高位在后,低位在前
        {
            printf("%c",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

A:Dima and a Bad XOR

题目链接:https://vjudge.net/contest/297959#problem/A

题目大意:有一个矩阵(n*m),矩阵元素是非负数。我们要从每一行中找出一个数,使得这n个数异或不为0,输出TAK以及选择的每行的数的位置(可能答案不唯一)。如果无解则输出NIE。

题解:将每行的数插入set(内部自动有序且不含重复元素的容器)中,我们取*st[i].begin()异或,如果非0,有解,输出对应位置。如果为0,我们就遍历一遍st[i].size,如果有大于1的,那就是有解,输出对应位置。如果所有行都只有一个不同数,则无解。

其实就是有两种情况:

第一种:每行都只有一个不同数,如果异或为0,则无解;否则有解。

第二种:存在有两个或两个以上的不同数的行。现在我们假设有且只有一行Rx,存在两个不同数Num1,Num2。并且我们仍然按照前边说的将set中的第一个元素异或,结果为0。这个时候我们假设除了Rx的所有行的异或结果为X,X^Num1=0,那么X=Num1,并且我们知道Num1!=Num2,因此X^Num2!=0。

因此,我们很容易可以知道,第二种情况中,只要存在一行有两个或两个以上的不同数,那么题目就是肯定有解的(当然多行也肯定有解啦)

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define P(x) x>0?x:0
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
const int maxn=505;

int n,m;
int a[maxn][maxn];
int ans[maxn];
set<int> st[maxn];

void init()
{
    for(int i=0;i<n;i++)
    {
        st[i].clear();
    }
}
void pos()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(a[i][j]==*st[i].begin())
                ans[i]=j+1;
        }
    }
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                scanf("%d",&a[i][j]);
                st[i].insert(a[i][j]);
            }
        }
        int temp=*st[0].begin();
        for(int i=1;i<n;i++)
        {
            temp=temp^(*st[i].begin());
        }
        if(temp)
        {
            printf("TAK\n");
            pos();
            for(int i=0;i<n;i++)
            {
                printf("%d%c",ans[i]," \n"[i==n-1]);
            }
        }
        else
        {
            for(int i=0;i<n;i++)
            {
                if(st[i].size()>1)
                {
                    st[i].erase(*st[i].begin());
                    temp=temp^(*st[i].begin());
                    break;
                }
            }
            if(temp)
            {
                printf("TAK\n");
                pos();
                for(int i=0;i<n;i++)
                {
                    printf("%d%c",ans[i]," \n"[i==n-1]);
                }
            }
            else
            {
                printf("NIE\n");
            }
        }
    }
    return 0;
}

C:Equalize Them All

题目链接:https://vjudge.net/contest/297959#problem/C

题目大意:一个有n个元素的数组,我们可以进行 【1】ai=ai+|ai−aj|,【2】ai=ai−|ai−aj|这两种操作,其中|i−j|=1。问我们通过最少多少次操作使得数组的每个元素值相等。

题解:最优解肯定是将数组的所有元素转换为众数mode。首先我们找到众数mode,以及任意一个众数的位置pos_mode。我们从pos_mode往两边遍历,如果比mode小则进行操作【1】使得小数转换为众数,并更新pos_mode;如果比mode大,则进行操作【2】使得大数转换为众数,并更新pos_mode;如果相等,则直接更新pos_mode。中间用ans来记录操作的次数,用结构体anser数组来记录每次操作,最后输出即可。

在找众数的时候,for循环的i是mark的下标,然后我写的<=maxn,所以RE了(QAQ)两次。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define P(x) x>0?x:0
#define First(i,j) a[i]=a[i]+P(a[i]-a[j])
#define Second(i,j) a[i]=a[i]-P(a[i]-a[j])
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
const int maxn=200005;

ll a[maxn];
ll mark[maxn];
ll n;
ll mode,pos_mode;
ll pos_front,pos_back;
ll ans;
struct node{
    ll key,x,y;
    node(ll aa=0,ll bb=0,ll cc=0):key(aa), x(bb),y(cc){}
}anser[maxn];

int main()
{
    while(~scanf("%lld",&n))
    {
        ans=0;
        memset(mark,0,sizeof(mark));
        for(ll i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            mark[a[i]]++;
        }
        ll max_=-1;
        ll flag=0;
        for(ll i=1;i<=n;i++)
        {
            if(mark[a[i]]==n)
            {
                printf("0\n");
                flag=1;
                break;
            }
            if(mark[a[i]]>max_)
            {
                max_=mark[a[i]];
                mode=a[i];
                pos_mode=i;
            }
        }
        if(flag)
            continue;
        pos_front=pos_mode-1;
        pos_back=pos_mode+1;
        for(ll i=pos_front;i>=1;i--)
        {
            if(a[i]>mode)
            {
                Second(i,pos_mode);
                anser[ans]=node(2,i,pos_mode);
                ans++;
            }
            else if(a[i]<mode)
            {
                First(i,pos_mode);
                anser[ans]=node(1,i,pos_mode);
                ans++;
            }
            pos_mode=i;
        }
        pos_mode=pos_back-1;
        for(ll i=pos_back;i<=n;i++)
        {
            if(a[i]>mode)
            {
                Second(i,pos_mode);
                anser[ans]=node(2,i,pos_mode);
                ans++;
            }
            else if(a[i]<mode)
            {
                First(i,pos_mode);
                anser[ans]=node(1,i,pos_mode);
                ans++;
            }
            pos_mode=i;
        }
        printf("%lld\n",ans);
        for(ll i=0;i<ans;i++)
        {
            printf("%lld %lld %lld\n",anser[i].key,anser[i].x,anser[i].y);
        }
    }
    return 0;
}

D:Superhero Battle

题目链接:https://vjudge.net/contest/297959#problem/D

题目大意:英雄打怪。怪兽有H个生命值,每一回合有n分钟,每分钟怪兽会受到不同的伤害d[i](可正可负),回合是依次循环的。问多少分钟后怪兽被kill,如果不能则输出-1。

题解:我们将一个回合的伤害前缀和存起来,存到sum数组中,那么sum[[n]则是一个整回合的净伤害。然后记录最大的伤害值为min_,并且记录最大伤害值出现在每个回合的时间pos_min。假设当前生命值的状态是hp0,hp1,hp2,hp3,...,hpn。

hp0=H

hp1=hp0+sum[n]

hp2=hp0+2*sum[n]

hp3=hp0+3*sum[n]

……

hpn=hp0+n*sum[n]

由此推得hpk=hp0+k*sum[n]

我们可以很容易理解,当hpi<= - min_时,怪兽就会在第i+1轮中死掉,最多活到i+1轮的pos_min时。

因此我们可以得到:第k轮hpk=hp0+k*sum[n]<= - min_时,怪兽将会在k+1轮中死掉。

hpk=hp0+k*sum[n]<= - min_

k>= (- min_ - hp0) / sum[n]

当然这里的话,sum[n]是负数,所以我们可以得到:

k>= (min_  + hp0) / abs(sum[n])

这里k要向上取整。我们可以这样想:在第k轮的时候hpk<=min_才能使怪兽在k+1轮死掉。如果我们的k向下取整,那么此时取整后的第k轮不能有hpk<=min_,所以k要向上取整。

当然,这上面的情况不包括特殊情况

1.永远死不掉:(1).最大的伤害min_>=0;(2).最大的伤害-H<min_<0(也就是第一轮死不掉),并且sum[n]>=0(也就是每轮的净伤害为正)

2.第一轮死掉:最大的伤害值min_<=-H并且sum[n]>=0

尤其是第二种情况不易想到吧,如果不特判,sum[n]可能为0,就会出现RE。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define P(x) x>0?x:0
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
const int maxn=2e5+5;

ll H,n;
ll d[maxn];
ll sum[maxn];
ll ans;
ll pos_min;
ll min_;

int main()
{
    while(~scanf("%lld%lld",&H,&n))
    {
        memset(sum,0, sizeof(sum));
        min_=INF;
        for(ll i=1;i<=n;i++)
        {
            scanf("%lld",&d[i]);
            sum[i]=sum[i-1]+d[i];
            if(sum[i]<min_)
            {
                min_=sum[i];
                pos_min=i;
            }
        }
        if(min_>=0||(min_<0 && abs(min_)<H && sum[n]>=0))//不能打死的情况
        {
            printf("-1\n");
            continue;
        }
        //在第一轮打死
        else if(sum[n]>=0)
        {
            ans=0;
            for(ll i=1;i<=n;i++)
            {
                if(H+sum[i]<=0)
                {
                    ans+=i;
                    printf("%lld\n",ans);
                    break;
                }
            }
        }
        else
        {
            ll quo=P((H + min_)/abs(sum[n]));
            if(P((H+min_)%abs(sum[n]))>0)
            {
                quo++;
            }
            ans=quo*n;
            ll rem=H+quo*sum[n];
            for(ll i=1;i<=n;i++)
            {
                if(rem+sum[i]<=0)
                {
                    ans+=i;
                    printf("%lld\n",ans);
                    break;
                }
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/89600678