Codeforces Round #666 (Div. 1) / contest 1396


ABCDE

( √:做出; ●:尝试未做出; ○:已补题 )


题目地址:https://codeforces.com/contest/1396



A Multiples of Length

题意:给定一个长度为 n 的数组 a,你可以操作恰好 3 次,每次操作如下:

  • 选择 a 的一个子数组(假设长度为 len),然后对这个子数组的每一个数,可以加上 len 的某个倍数。

进行完 3 次操作之后,要求 a 的所有元素都为 0 。

你需要输出这 3 次操作的方案。

思路:前两次操作之后一定要使得 a 的所有元素都是 n 的倍数,这样最后一次操作就可以一次性完成了。考虑把数组分为第一个和后面 n-1 个两段(这么分是为了保证 1 和 n-1 都和 n 互质),然后假设考虑第一个数,我们要使得 a 1 + l e n × x = n × y a_1+len\times x=n\times y a1+len×x=n×y ,这运用扩展欧几里得的算法就能解出来。后面的 n-1 个数也同理。

代码

#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#include <cstdio>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef std::vector<int> VI;
typedef std::pair<int,int> P;
int read()
{
    int x=0,flag=1; char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=1e5+5;
LL a[maxn];
int n;

LL ex_gcd(LL a,LL b,LL &x,LL &y)
{
    if(!b) {x=1; y=0; return a;}
    LL r=ex_gcd(b,a%b,x,y),t=x;
    x=y,y=t-a/b*y;
    return r;
}

int main()
{
    n=read();
    REP(i,1,n) a[i]=read();
    if(n==1)
    {
        printf("1 1\n1314\n");
        a[1]+=1314;
        printf("1 1\n12\n");
        a[1]+=12;
        printf("1 1\n%lld",-a[1]);
    }
    else
    {
        printf("1 1\n");
        LL x,y;
        ex_gcd(n,1,x,y);
        y=-y;
        printf("%lld\n",y*a[1]);
        a[1]+=y*a[1];

        printf("2 %d\n",n);
        ex_gcd(n,n-1,x,y);
        y=-y;
        for(int i=2;i<=n;i++)
        {
            printf("%lld ",y*a[i]*(n-1));
            a[i]+=y*a[i]*(n-1);
        }
        puts("");

        printf("%d %d\n",1,n);
        REP(i,1,n) printf("%lld ",-a[i]);
    }

    return 0;
}



B Stoned Game

题意:给出 n 堆石头,每堆石头有若干个。T 和 HL 开始玩游戏,每次每个人可以从任意一堆中拿走一个石头,但是不能拿上一次另外一个人拿过的那一堆,最后谁不能拿谁就输。T 先拿,问最后谁赢?

思路:如果只有 1堆,那么就是先手必胜。考虑如果其中某一堆比其他所有加起来还要多,那么也是先手必胜(他只要一直拿最多的那一堆就行了);从上可以看出出现某一堆最多的,这就是一个不稳定态,出现这种状态的话,就会有必胜方案,所以对于一般情况,如果总数为奇数,那么先手必胜(全部拿完)。

代码

#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#include <cstdio>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef std::vector<int> VI;
typedef std::pair<int,int> P;
int read()
{
    int x=0,flag=1; char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

int a[105],n;

int main()
{
    int T=read();
    while(T--)
    {
        n=read();
        REP(i,1,n) a[i]=read();
        if(n==1) puts("T");
        else
        {
            int sum=0;
            int m=*max_element(a+1,a+n+1);
            REP(i,1,n) sum+=a[i];
            if((sum%2==1) || m*2>sum) puts("T");
            else puts("HL");
        }
    }

    return 0;
}



C Monster Invaders

题意:有两种怪兽,一种是小怪(1滴血),另一种是 boss(2滴血);有 3 种武器,第一种可以对任意怪兽造成 1 点伤害,第二种可以对所有怪兽造成 1 点伤害,第三种可以对任意怪兽秒杀,这三种武器的使用一次的时间分别为 r1, r2, r3( r 1 ≤ r 2 ≤ r 3 r1 \le r2 \le r3 r1r2r3) 。现在有 n 层关卡,层与层之间相互相连,在某一层的时候只能去相邻的另一层,并且去另一层耗时为 d 。在每一层都有 1 只 boss 和 a i a_i ai 只小怪。在每一层的时候,如果没有杀死所有小怪,你不能直接对 boss 使用第一种或者第三种武器,并且如果你对 boss 造成了伤害,但是没有杀死 boss,你会被强制要求转移到相邻的另一层去。现在一开始你在第一层,你的目标是杀死每一层的 boss,问最少需要多长时间?

思路:题意非常非常复杂,不过在某一层的时候,我们事实上只有三种击杀方案:

  • 对所有小怪使用 r1,对 boss 使用 r3(直接杀死);
  • 对所有小怪使用 r1,对 boss 使用 r1,然后回来的时候再对 boss 使用 r1(间接杀死);
  • 对所有怪兽使用 r2,然后回来的时候再对 boss 使用 r1(间接杀死);

然后对于每一层,我们就能算出一个东西 f ( i , j ) f(i,j) f(i,j) ,它表示第 i 层直接杀死(j=1)和间接杀死(j=0)所需要的最少时间是多少。然后再设 g ( i ) g(i) g(i) 表示前 i 层杀死 boss 所需最少时间,这里 g ( i ) g(i) g(i) 实际上只有三种转移:

  • g ( i − 1 ) g(i-1) g(i1) + 第 i 层直接杀死;
  • g ( i − 1 ) g(i-1) g(i1) + 第 i 层间接杀死 + 2d ;
  • g ( i − 2 ) g(i-2) g(i2) + 第 i 层和第 i-1 层间接杀死 + 2d;

然后就是要注意末尾的地方,不一定在第 n 层结尾,所以还要考虑第 n-1 层结尾时可能更优(也就是第 n-1 层间接杀死,第 n 层直接杀死的情况,就不用回去了)。这里不需要考虑第 n-2 以及更前结尾的情况,因为第三种转移已经可以处理了。

代码

#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#include <cstdio>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef std::vector<int> VI;
typedef std::pair<int,int> P;
int read()
{
    int x=0,flag=1; char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=1e6+5;
LL a[maxn],r1,r2,r3,n,ans,d;
LL f[maxn][2],g[maxn];

int main()
{
    n=read(); r1=read(),r2=read(),r3=read(); d=read();
    REP(i,1,n) a[i]=read();
    f[0][0]=f[0][1]=0;
    REP(i,1,n)
    {
        f[i][0]=f[i][1]=3e18;
        f[i][1]=min(f[i][1],r1*a[i]+r3);
        f[i][0]=min(f[i][0],r1*a[i]+r1);
        f[i][0]=min(f[i][0],r2);
    }
    //REP(i,1,n) cout<<f[i][0]<<' '<<f[i][1]<<endl;
    REP(i,1,n)
    {
        g[i]=g[i-1]+f[i][1];
        g[i]=min(g[i],g[i-1]+f[i][0]+d*2+r1);
        if(i>1) g[i]=min(g[i],g[i-2]+f[i][0]+f[i-1][0]+r1*2+d*2);
    }
    if(n>1) g[n]=min(g[n],g[n-2]+f[n-1][0]+f[n][1]+d+r1);
    cout<<g[n]+d*(n-1);

    return 0;
}



D

题意

思路

代码




E

题意

思路

代码


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值