Codeforces #278 Div 1 簡要題解

比賽總結

這次div1打得還是很慘,2個小時裏一直在wa A題,wa了七八次才ac,罰時太慘。。。
手速真的非常重要啊

A. Fight the Monster

題目鏈接

http://codeforces.com/contest/487/problem/A

題目大意

奧特曼打小怪獸,每局開始時,雙方都會掉血,各自掉max(0,ATKYDEFM),max(0,ATKMDEFY) ,初始時奧特曼血量是HPY ,小怪獸血量是HPM 。兩個傢伙誰先沒血誰先輸。但是奧特曼可以開掛,可以花a 元錢讓ATKY 加1,花d 元錢讓DEFY 加1,花h 元錢讓HPY 加1,問最少花多少錢,他才能打敗小怪獸

思路

剛開始我很sb地去寫了個二分,結果數據太硬,如果直接暴力枚舉奧特曼的三個參數的大小的話,要麼範圍枚舉小了會wa,要麼範圍枚舉大了又會TLE

其實沒必要二分,也沒必要把奧特曼的三個參數都枚舉,只需要枚舉DEFYATKY ,然後計算出最少所需的HPY ,然後計算出這樣的方案要花多少錢,更新答案就好了。

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define INF 0x7fffffffffffffff

using namespace std;

typedef long long int LL;

LL HPy,ATKy,DEFy,HPm,ATKm,DEFm,h,a,d;
LL ans=INF;

inline void check()
{
        for(LL aa=0;aa<=1000;aa++)
            for(LL dd=0;dd<=1000;dd++)
            {
                LL hh;
                ATKy+=aa;
                DEFy+=dd;

                LL lifey,lifem;
                if(max((LL)0,ATKy-DEFm)==(LL)0)
                {
                    ATKy-=aa;
                    DEFy-=dd;
                    continue;
                }
                else
                {
                    lifem=HPm/max((LL)0,ATKy-DEFm);
                    if(HPm%max((LL)0,ATKy-DEFm)) lifem++;
                }
                if(max((LL)0,ATKm-DEFy)==(LL)0) hh=(LL)0;
                else
                {
                    lifey=1+lifem*max((LL)0,ATKm-DEFy);
                    if(HPy<lifey) hh=lifey-HPy;
                    else hh=0;
                }
                ATKy-=aa;
                DEFy-=dd;
                if(hh*h+aa*a+dd*d<ans) ans=hh*h+aa*a+dd*d;
            }
}

int main()
{
    scanf("%I64d%I64d%I64d%I64d%I64d%I64d%I64d%I64d%I64d",&HPy,&ATKy,&DEFy,&HPm,&ATKm,&DEFm,&h,&a,&d);
    check();
    printf("%I64d\n",ans);
    return 0;
}

B. Strip

題目鏈接

http://codeforces.com/contest/487/problem/B

題目大意

給你一個序列a ,要你將它分成連續的若干段,每一段長度大於等於l ,最大值減最小值小於等於s 。問最少分成多少段。

思路

膜用線段樹幹掉此題的各位大爺。。。

蒟蒻只會用set亂搞。。。

首先我們求出L[i]= 分出的一段子序列,右端點爲i ,左端點最遠的位置。顯然L[i] 具有單調性,即i<j,L[i]L[j] ,這個大家自己腦補下就能明白。於是我們可以掃一遍整個序列,從小到大枚舉序列的下標i ,維護一個multiset,其中的所有元素的最大值減最小值小於等於s ,並維護一個變量now ,代表這個multiset裏維護的元素是屬於區間[now,i1] 的,如果a[i] 加入後,集合裏最大值減最小值大於s ,則需要在multiset裏刪除掉a[now]a[now] ,如此反覆直到最大值減最小值小於等於s 爲止,此時L[i]=now

然後我們再求出f[i]= 前i個數字的最少劃分次數。裸的做法是O(n2) 的,但是我們這裏可以再次用multiset將其優化到O(nlogn) ,還是用到了L[i] 的單調性。我們可以掃一遍序列a ,從小到大枚舉序列的下標i ,在集合裏維護[L[i],il] 段的f[] 值,每次從中取最小值做DP即可。若取不出一個f 值的話,就賦f[i]=inf ,表明這個狀態無效了

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <set>

#define MAXN 110000
#define INF 0x3f3f3f3f

using namespace std;

//int maxv[MAXN<<2],minv[MAXN<<2];
int a[MAXN],n;

int f[MAXN],L[MAXN]; //L[i]=右端點爲i,左端點最遠劃分的位置

multiset<int>bst;

int main()
{
    int l,s;
    scanf("%d%d%d",&n,&s,&l);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int now=1;
    for(int i=1;i<=n;i++)
    {
        while(!bst.empty()) //bst裏保存的是[now,i-1]裏的點
        {
            int minv=*bst.begin(),maxv=*(--bst.end()); //[now,i-1]裏的最大值和最小值
            minv=min(minv,a[i]),maxv=max(maxv,a[i]);
            if(maxv-minv<=s) break;
            bst.erase(bst.lower_bound(a[now]));
            now++;
        }
        L[i]=now;
        bst.insert(a[i]);
    }
    bst.clear(); //之後multiset裏保存的是每個位置上的f值
    now=0; //multiset裏保存的是[now,i-1]的f值
    for(int i=1;i<=n;i++)
    {
        if(i-l>=0) bst.insert(f[i-l]);
        while(now<=i-l&&now<L[i]-1&&!bst.empty())
        {
            //cout<<now<<endl;
            bst.erase(bst.lower_bound(f[now]));
            now++;
        }
        if(bst.empty())
            f[i]=INF;
        else f[i]=*bst.begin()+1;
    }
    if(f[n]>=INF) printf("-1\n");
    else printf("%d\n",f[n]);
    return 0;
}

C. Prefix Product Sequence

題目鏈接

http://codeforces.com/contest/487/problem/C

題目大意

定義前綴和pre[i]=ij=1a[j]modn ,要你構造序列[1,2,...,n] 的一個排列序列a ,使得a 的前綴和組成的序列是[0,1,2,...,n1] 的一個排列

思路

顯然若n 爲合數的話(4除外),無論怎麼構造,得到的前綴和序列肯定是有重複元素的,這和hash的模數不能取合數的道理是一樣的。而由於4比較特殊,它是正整數裏的第一個合數,因此沒有這個問題,我們可以手玩出n=4的解

其他情況下,顯然a[1]=1,a[n]=n ,剩餘的部分,我們這樣構造:a[i]=irev[i1]modn ,這樣的話,就能在mod n的意義下使第i 項前綴積避免受到第i1 及以前的元素的影響,保證每一項前綴積不一樣

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 110000

using namespace std;

typedef long long int LL;

LL a[MAXN],rev[MAXN];
int n;

bool isPrime(int x)
{
    for(int i=2;i*i<=x;i++)
        if(!(x%i)) //!!!!!!
            return false;
    return true;
}

int main()
{
    scanf("%d",&n);
    if(n==4)
    {
        printf("YES\n1\n3\n2\n4\n");
        return 0;
    }
    if(!isPrime(n))
    {
        printf("NO\n");
        return 0;
    }
    printf("YES\n");

    rev[1]=1;
    for(int i=2;i<=n;i++)
        rev[i]=rev[n%i]*rev[n%i]%n*i%n*(n/i)%n*(n/i)%n;
    a[1]=1,a[n]=n;
    for(int i=2;i<n;i++) a[i]=i*rev[i-1]%n;
    for(int i=1;i<=n;i++) printf("%I64d\n",a[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章