1. 程式人生 > >AtCoder Regular Contest 095

AtCoder Regular Contest 095

pow 滿足 string span 有一個 條件 i++ 就是 algo

AtCoder Regular Contest 095


C - Many Medians

題意:

有A,B兩種匹薩和三種購買方案,買一個A,買一個B,買半個A和半個B,花費分別為a,b,c。

求買X個A和Y個B最小花費使多少。

分析:

明顯的發現肯定買性價比更高的方案,分情況討論一下,如果\(a+b<=2*c\),那麽明顯的先買足c到A,B中較小的一個,然後再比較一下剩下的那個的單價和\(2*c\)的大小。

A[ans=] -->|a+b<=2*c| B(A*a+B*b)
A --> |else| C{2*c}
C --> |A<=B| D[*A]
C --> |else| E[*B]
D --> |b<=2*c| F[+b*B-b*A]
D --> |else| G[+2*c*B-2*c*A]
E --> |a<=2*c| H[+a*A-a*B]
E --> |else| I[+2*c*A-2*c*B]
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define MAXN 4000007
#define mo 19930726
#define ll long long
using namespace std;
typedef unsigned long long ull;
#define ms(arr) memset(arr, 0, sizeof(arr))
const int inf = 0x3f3f3f3f;
ll a,b,c,x,y,ans;
int main()
{
    cin>>a>>b>>c>>x>>y;
    ll minn=min(x,y);
    if(a+b<=2*c){
        cout<<x*a+y*b;
    } else {
        ans+=minn*c*2;
        if(x<y){
            if(2*c<=b) b=2*c;
            ans+=b*(y-x);
        } else {
            if(2*c<=a) a=2*c;
            ans+=a*(x-y);
        }
        cout<<ans;
    }
}

D - Static Sushi

題意:

有n個壽司圍成一圈,給出壽司離起點的距離v和壽司的價值x,從起點開始走,任意時刻都可以決定走的方向,並且可以在任意時刻停下,求\(max(\sum x-\sum v)\)

分析:

可以考慮我們一共有幾種選擇,可以發現最優的情況一定是向一個方向走然後掉頭走另一個方向。前綴和求一下,順時針逆時針直接搞搞也就出來了。

#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define MAXN 200007
#define mo 19930726
using namespace std;
typedef unsigned long long ull;
#define ms(arr) memset(arr, 0, sizeof(arr))
#define ll long long
const int inf = 0x3f3f3f3f;
ll n,v[MAXN],x[MAXN],C,sum[MAXN],pre[MAXN],ans;
int main()
{
    cin>>n>>C;
    for(int i=1;i<=n;i++){
        cin>>x[i]>>v[i];
        sum[i]=sum[i-1]+v[i];
        pre[i]=max(pre[i-1],sum[i]-x[i]);
    }
    ll cnt=0;
    ll c=C;
    for(int i=n;i>=1;i--){
        ans=max(ans,cnt+pre[i]);
        cnt+=v[i]-2*(c-x[i]);
        c=x[i];
    }
    for(int i=1;i<=n;i++) x[i]=C-x[i];
    reverse(x+1,x+n+1);
    reverse(v+1,v+n+1);
    for(int i=1;i<=n;i++){
        sum[i]=sum[i-1]+v[i];
        pre[i]=max(pre[i-1],sum[i]-x[i]);
    }
    cnt=0;
    c=C;
    for(int i=n;i>=1;i--){
        ans=max(ans,cnt+pre[i]);
        cnt+=v[i]-2*(c-x[i]);
        c=x[i];
    }
    cout<<ans;
}

E - Everything on It

題意:

給你n種醬,可以任意添加入拉面中。你可以有任意碗拉面,然後你可以選擇加入和不加這n種醬的任意一種進去任意一碗拉面中,問你怎麽添加,使得:

1.這任意碗拉面中,不存在兩碗拉面加的醬相同。

2.這任意碗拉面中,每一種醬都至少加入過兩碗拉面中。

滿足以上兩個條件的加醬方法算一種方案。問有多少種方案%mod。

分析:

不難看出這題應該是一個容斥。

我們不犯設\(f[i]\)為有i種醬不合條件時的方案數,那麽明顯可以求出答案是\(\sum_{i=0}^n (-1)^if[i]C_n^i\)

那麽明顯我們只需要求出\(f\)就可以輕松得到ans。

建立另一個數組\(g[i][j]\)表示前j碗拉面中有i種醬不符合條件的方案數。我們可以用類第二類斯特林數的方式求出來,\(g[i][j]=g[i-1][j-1]+g[i-1][j]*(j+1)\)

可以這麽想,當我們遞推過來時,如果前\(i-1\)種醬在\(j-1\)碗面中,那麽第\(i\)種醬一定在第\(j\)碗面中。如果前\(i-1\)種醬在前\(j\)碗面中,那麽第\(i\)種醬就可在任何一碗面中或者壓根就不在任何一碗面中。

之後求出\(f[i]=\sum_{j=0}^ig[i][j]2^{(n-i)j} 2^{2^{(n-i)}}\)

然後用這個公式
\(a^n \equiv a^{n\cdot mod(p-1)}(modp)\)

再直接往\(\sum_{i=0}^n (-1)^if[i]C_n^i\)這東西裏面套就行了。

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <iostream>
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define MAXN 3007
#define ll long long
using namespace std;
typedef unsigned long long ull;
#define ms(arr) memset(arr, 0, sizeof(arr))
const int inf = 0x3f3f3f3f;
ll c[MAXN][MAXN];
ll g[MAXN][MAXN];
ll n,mo;
inline ll niubide_power(ll k,ll x,ll mod)
{
    ll ans=1;
    while(x) {
        if(x&1ll) ans=ans*k%mod;
        k=(k*k%mod);
        x>>=1;
    }
    return ans;
}
int main()
{
    cin>>n>>mo;
    for(re int i=1;i<=n;i++){
        c[i][0]=1;c[i][i]=1;
        for(int j=1;j<i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mo;
    }
    for(int i=0;i<=n;i++){
        g[i][0]=1;
        for(int j=1;j<=i;j++)
            g[i][j]=(g[i-1][j-1]+g[i-1][j]*(j+1)%mo)%mo;
    }
    ll ans=0;
    for(int i=0;i<=n;i++){
        ll k=c[n][i];
        if(i&1) k=(mo-k)%mo;
        ll kind=niubide_power(2,n-i,mo);
        ll x=niubide_power(2,n-i,mo-1);
        x=niubide_power(2,x,mo);
        ll cnt=0,y=1;
        for(int j=0;j<=i;j++){
            cnt=(cnt+(g[i][j]*y)%mo)%mo;
            y=kind*y%mo;
        }
        ans=(ans+(k*cnt)%mo*x%mo)%mo;
    }
    cout<<ans;
}

F - Sweet Alchemy

題意:

\(n≤50\) 的樹,每個點有權值,現要選點(可多次選一個點)使點數盡量多,如下限制:
選的總權值不超過\(C≤1e9\)\(c_i\) 表示\(i\)選的次數,\(p_i\) 表示 \(i\) 的父親,那麽\(c_{p[i]}≤c_i≤c_{p[i]}+D\)
\(D≤1e9\)是給定常數。

分析:

首先我們可以差分一下這棵樹,然後這個題就變成了:在一棵樹上有不超過50 個節點,每個節點均有一個權值及一個代價,除1號節點外每個節點選擇的次數均不能超過 D。求在總代價不超過x的前提下,如何使權值最大化?

看上去像是一個經典的背包問題,但是超大的背包容量你也背不起來。

突破口貌似是\(n\le50\)這極少的物品個數。

但是突破不了了。

看了題解覺得真是非常神仙。

首先背包問題有一個經典錯解,就是那個性價比高選哪個,但是明顯錯誤的就是這樣可能會造成包內有大量剩余空間沒得使用浪費掉,但實際上是有更優秀的解的。

考慮什麽時候我們能用性價比更高的物品替換到性價比低的。假設我們有\(v_i\),\(w_i\)\(v_j\),\(w_j\),且\(\frac{v_i}{w_i}>\frac{v_j}{w_j}\)。那麽我們在選了\(v_i\)\(j\)物品,就相當於選了\(v_j\)\(i\)物品。因為權值沒有變化,但是代價卻更小了。

所以我們不能直接替換的最多只有\(v_i-1\)這麽多,而\(v\)的足夠小的範圍是可以接受的。所以直接把每件物品都取\(min(D,n)\)件然後多重背包就可以了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define re register
#define MAXN 507
#define mo 19930726
#define ll long long
#define int ll
using namespace std;
typedef unsigned long long ull;
#define ms(arr) memset(arr, 0, sizeof(arr))
const int inf = 0x3f3f3f3f;
int n,X,D,ans,num,dp[125500];
int w[MAXN],val[MAXN],pos[MAXN],head[MAXN];
int tot,V[MAXN*MAXN],W[MAXN*MAXN],cnt;
struct po
{
    int nxt,to;
}edge[MAXN<<1];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void add_edge(int from,int to)
{
    edge[++num].nxt=head[from];
    edge[num].to=to;
    head[from]=num;
}
void dfs(int u)
{
    val[u]=1;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        dfs(v);
        w[u]+=w[v];val[u]+=val[v];
    }
    cnt+=val[u]*n; pos[u]=u;
}
inline bool cmp(int x,int y){return val[x]*w[y]>val[y]*w[x];}
main()
{
    n=read();X=read();D=read();
    w[1]=read();
    for(re int i=2;i<=n;i++){
        w[i]=read();int x=read();
        add_edge(x,i);
    }
    dfs(1);
    sort(pos+1,pos+n+1,cmp);
    int tmp=min(n,D);
    for(re int i=1;i<=cnt;i++) dp[i]=inf;
    for(re int i=1;i<=n;i++){
        int len=1,lim=tmp;
        while(lim>=len){
            V[++tot]=len*val[pos[i]];
            W[tot]=len*w[pos[i]];
            lim-=len,len<<=1;
        }
        if(lim) V[++tot]=lim*val[pos[i]],W[tot]=lim*w[pos[i]];
    }
    for(int i=1;i<=tot;i++)
        for(int j=cnt;~j;j--){
            if(j>=V[i]) dp[j]=min(dp[j],dp[j-V[i]]+W[i]);
        }
    int ans=0;
    for(int i=0;i<=cnt;i++){
        if(dp[i]>X) continue;
        int ret=i,left=X-dp[i];
        for(int j=1;j<=n;j++){
            int tem=pos[j],used=min(max(D-n,0ll),left/w[tem]);
            if(tem==1) used=left/w[tem];
            left-=w[tem]*used;
            ret+=val[tem]*used;
        }
        ans=max(ans,ret);
    }
    cout<<ans;
}

AtCoder Regular Contest 095