AtCoder Regular Contest 095

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;
}

原文地址:https://www.cnblogs.com/victorique/p/9571302.html

时间: 2024-11-02 12:35:59

AtCoder Regular Contest 095的相关文章

AtCoder Regular Contest 098

AtCoder Regular Contest 098 C - Attention 题意 给定一个只包含"E","W"字符串,可以花一的花费使他们互相转换.选定一个位置,使位置左边的字符都变成E,右边都变成W所需要的最小花费. 分析 这题纯粹是签到题,做两个前缀和然后直接加就可以了. #include <iostream> #include <cmath> #include <cstring> #include <cstdi

AtCoder Regular Contest 094

AtCoder Regular Contest 094 C - Same Integers 题意: 给定\(a,b,c\)三个数,可以进行两个操作:1.把一个数+2:2.把任意两个数+1.求最少需要几次操作将三个数变为相同的数. 分析: 可以发现如果三个数的奇偶性相同直接加就可以了,对于奇偶性不同的,先把奇偶性相同的两个数都+1,然后按照相同的处理就可以了.可以证明没有更好的方案. #include <bits/stdc++.h> using namespace std; int a,b,c,

AtCoder Regular Contest 103

AtCoder Regular Contest 103 一些吐槽 参加的第一场\(ARC\):一个模拟 + 三个构造 没见过比这更令人感动的题型设置了(简直就是针对我(TAT)) . 感觉全场就我一个人\(E\)题WA了四遍才过....... C-//// 题目大意: 网址 给定一个串\(S\),要求修改一些字符,使得串满足以下条件: \(S_i = S_{i+2}\) \(S_1 \neq S_2\) . 问最少需要修改多少个字符. 题解: 无脑统计一下奇数和偶数格的每种种类. 然后在最大值和

AtCoder Regular Contest 075 E - Meaningful Mean 树状数组求顺序对, 前缀和

题目链接: http://arc075.contest.atcoder.jp/tasks/arc075_c 题意: 给你一个序列和一个数k,求有多少对l,r,使得a[l]+a[l+1]+...+a[r]的算术平均数大于等于k 1≤N≤2×10^5 1≤K≤10^9 1≤ai≤10^9 思路: 首先对于所有数减去k,这样就不用除(r-l+1), 然后我们发现所求的就是有多少对l,r,使得sum[r]-sum[l-1] >= 0, sum是减去k之后的序列的前缀和 用树状数组对sum求有多少个顺序对

AtCoder Regular Contest 063 E:Integers on a Tree

题目传送门:https://arc063.contest.atcoder.jp/tasks/arc063_c 题目翻译 给你一个树,上面有\(k\)个点有权值,问你是否能把剩下的\(n-k\)个点全部填上权值,使得每条边链接的两个点权值相差\(1\),如果可以做到需要输出任意一组方案. 题解 我们考虑每条边权值为\(1\)或\(-1\),那么相当于黑白染色一样,所有点权值的奇偶性也都是确定的.如果与读入的\(k\)个点中某个点相冲突了就\(GG\).另外每个点的取值范围都可以转化成一段区间\([

AtCoder Regular Contest 062 E - AtCoDeerくんと立方体づくり / Building Cubes with AtCoDeer

题目传送门:https://arc062.contest.atcoder.jp/tasks/arc062_c 题目大意: 给你\(N\)块正方形木板,每块木板四角有四种颜色(可以相同),木板中央有编号,求选出6块不同的板子,围成的本质不同的合法立方体的个数.一个合法立方体,当且仅当木板有编号的一面在外面,且立方体顶点处的三个颜色相同.由于编号的存在,木板可以有4种形态.两个立方体本质相同,当且仅当存在一种空间旋转方式,使得两个立方体一模一样(包括编号方向) 没想到这题巨暴力--当我们确定对面的两

AtCoder Regular Contest 072 E:Alice in linear land

题目传送门:https://arc072.contest.atcoder.jp/tasks/arc072_c 题目翻译 给你一个数组\(D\),然后给你一个操作序列\(d\),每次操作可以将\(D\)变成\(min(D,|D-d[i]|)\).假如这一个操作序列执行完了之后你的\(D\)变成\(0\)了,那么就称这个操作序列是合法的.现在有\(Q\)个询问,每个询问由一个\(q[i]\)表示,问你假如你可以把\(d[i]\)变成任意正整数,你能否将这个操作序列变成不合法的.\(N,Q\leqsl

AtCoder Regular Contest 101 F

题目链接:atcoder 考虑所有的洞将数轴划分成了若干个区间,则对每个机器人无论他怎么移动都不可能出这个区间,所以每个机器人至多只可能掉入两个洞中 对于最左边和最右边没有洞的机器人,显然他们的掉洞方案唯一,于是我们不去考虑它,对于剩下的机器人,我们用一个二元组\((l_i,r_i)\)表示它到离它最近的左/右边的洞的距离,很明显一个机器人掉入哪个洞只与操作序列达到的最左边/右边的位置有关. 将所有的二元组放在一个平面上,用它们来标记一些点,操作序列的历史达到的最左边/右边的位置可以用一条只会向

AtCoder Regular Contest 083 D:Restoring Road Network

In Takahashi Kingdom, which once existed, there are N cities, and some pairs of cities are connected bidirectionally by roads. The following are known about the road network: People traveled between cities only through roads. It was possible to reach