[ARC 066] Tutorial

Link:

ARC 066 傳送門c++

C:

若是存在可行方案則答案爲$2^{n/2}$ide

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+10,MOD=1e9+7;
int n,x,res[MAXN];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        int rk=(n-1+x)/2;
        if(!res[rk+1]) res[rk+1]=i;
        else if(!res[n-rk]) res[n-rk]=i;
        else return puts("0"),0;
    }
    int res=1;
    for(int i=1;i<=n/2;i++) (res*=2)%=MOD;
    printf("%d",res);
    return 0;
}
Problem C

 

D:

挺不錯的一道數位$dp$spa

因爲沒法直接計算$sum$和$xor$的對數,所以考慮枚舉$a,b$,而將$sum,xor\le n$做爲限制條件3d

又由於公式:$a+b=aXORb+2*(a\&b)$,因此$a+b\le aXORb$,只考慮$a+b$的限制便可code

此時問題轉化爲對於每一個$sum\le n$求$xor$的取值個數blog

這樣就能夠用$dp[i][s]$表示前$i$位肯定,$a+b$的和爲$s$的個數,每次分$a,b$在該位上總共有幾個1轉移get

(按每位1的個數轉移纔不會考慮異或與和同時相同的狀況!)it

 

但這樣複雜度是不對的,在枚舉$s$上明顯花費了沒必要要的時間io

根據通常數位$dp$記錄上限的思想,若是前$i$位的$n-s\ge 2$,這些數之後都保證合法,就能統一計算了event

這樣就從$dp[i][s]$變成了$dp[i][0/1/2]$

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
#define MAX_D 64
#define MOD ((ll)1e9 + 7)
ll N,dp[MAX_D][3],res;int nxt;

int main() 
{    
    scanf("%lld",&N);
    dp[MAX_D-1][0]=1;
    for(int i=MAX_D-1;i>0;i--) 
        for(int j=0; j <= 2; j++)
            for(int k=0;k<=2;k++) 
            {
                nxt=j*2+((N>>(i-1))&1)-k;
                if (nxt<0) continue;
                nxt=nxt>2?2:nxt;
                (dp[i-1][nxt]+=dp[i][j])%=MOD;
            }
    res=0;
    for (int i=0;i<=2;i++) (res+=dp[0][i])%=MOD;
    printf("%lld\n", res);
    return 0;
}
Solution A

從後往前用記憶化搜索的形式寫起來更加方便

一開始將上限值就設爲$n$,每次肯定最後一位取幾個之後去掉最後一位,不用考慮和的合法性了

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
const int MOD=1e9+7;
map<ll,ll> dp;ll n;

ll dfs(ll x)
{
    if(dp.count(x)) return dp[x];
    return dp[x]=(dfs(x>>1)+dfs((x-1)>>1)+dfs((x-2)>>1))%MOD;
}

int main()
{
    scanf("%lld",&n);
    dp[0]=1;dp[1]=2;
    printf("%lld",dfs(n));
    return 0;
}
Solution B

 

E:

首先要觀察出幾點性質:

一、只有在減號後可能加括號

二、括號不可能嵌套超過兩層,不然能夠轉化爲只有兩層的簡化狀況

這樣就能夠記錄$dp[0/1/2]$分別表示當前還有幾個左括號未匹配的最大值來$dp$了

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
int n,x;char op;ll dp[3],nxt[3];

int main()
{
    scanf("%d%d",&n,&dp[0]);
    dp[1]=dp[2]=-1ll<<60;
    for(int i=1;i<n;i++)
    {
        scanf(" %c%d",&op,&x);
        if(op=='-') x=-x;
        nxt[0]=dp[0]+x,nxt[1]=dp[1]-x,nxt[2]=dp[2]+x;
        dp[0]=max(nxt[0],max(nxt[1],nxt[2]));
        if(op=='+') dp[1]=max(nxt[1],nxt[2]),dp[2]=nxt[2];
        else dp[1]=dp[0],dp[2]=max(nxt[1],nxt[2]);
    }
    printf("%lld",dp[0]);
    return 0;
}
Solution A

其實也能夠不用$dp$,考慮若是在某個減號後加了第一個括號的最優解

發現此時能保證將下一個減號後的值都變爲正貢獻,但對當前位到下一個減號間的值是無能爲力的

這樣枚舉第一個括號的位置對答案更新便可

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+10;
char op[MAXN];
int n,dat[MAXN],nxt[MAXN],fst;
ll suf[MAXN],cur,res=-1ll<<60;

int main()
{
    scanf("%d%d",&n,&fst);
    for(int i=1;i<n;i++)
        scanf(" %c%d",&op[i],&dat[i]);
    cur=n;
    for(int i=n-1;i;i--)
    {
        suf[i]=suf[i+1]+dat[i];
        if(op[i]=='-') nxt[i]=cur,cur=i;
    }
    cur=0;
    for(int i=1;i<n;i++)
        if(op[i]=='-') 
            res=max(res,cur-suf[i]+2*suf[nxt[i]]),cur-=dat[i];
        else cur+=dat[i];
    res=max(res,cur);
    printf("%lld",res+fst);
    return 0;
}
Solution B

 

F:

相關文章
相關標籤/搜索