• #13 CF1267G & CF653G & CF1091H


    Game Relics

    题目描述

    点此看题

    解法

    关键的 \(\tt observation\):由于 \(x\leq c_i\),那么最优策略一定是先疯狂抽卡,然后再买入剩下的东西。这是因为抽到什么其实都是赚的,那么为了让爆率更高一定先抽卡再买卡。

    因为在最优策略下买卡的顺序不影响答案,可以把题目等价转化下面的形式:

    • 第一类抽奖:如果抽中已经解锁的会花费 \(\frac{x}{2}\),否则会花费 \(x\) 并且解决卡牌 \(i\)
    • 第二类抽奖:如果抽中已经解锁的会花费 \(0\),否则会花费 \(c_i\) 并且解锁卡牌 \(i\)

    这两类抽奖达到的效果是完全一致的,所以我们只需要取花费的较小者即可。设剩下 \(k\) 张卡牌,代价总和是 \(c\),第一类抽奖新抽到一张牌的期望代价是 \((\frac{n}{k}+1)\cdot\frac{x}{2}\);第二类抽奖新抽到一张牌的期望代价是 \(\frac{c}{k}\)

    那么剩下我们只需要计算还剩 \(i\) 张牌,代价总和是 \(j\) 的概率,乘上代价就得到了期望。概率就是对应的方案数除以 \({n\choose i}\),那么用背包计算即可,时间复杂度 \(O(n^2\sum c)\)

    总结

    将不同的操作转化成相同的形式,便于发现后面的贪心策略;此外这种决策随着随机情况变化的题目,并不一定只能用 \(dp\) 解决。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 105;
    #define db long double
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,a[M];db x,ans,f[M][10005];
    int main()
    {
    	n=read();x=read();f[0][0]=1;
    	for(int i=1;i<=n;i++) m+=a[i]=read();
    	for(int i=1;i<=n;i++) for(int k=i;k>=1;k--)
    		for(int j=a[i];j<=m;j++)c++
    			f[k][j]+=f[k-1][j-a[i]]*1.0*k/(n-k+1);
    	for(int i=1;i<=n;i++) for(int j=0;j<=m;j++)
    		ans+=f[i][j]*min((db)j/i,((db)n/i+1)*x/2);
    	printf("%.10Lf\n",ans);
    }
    

    Move by Prime

    题目描述

    点此看题

    解法

    可以把所有质数分开考虑,设质数 \(p\) 的出现次数从大到小排序的结果是 \(x_i\),那么对于一个子序列一定选取其中位数,答案的形式为 \(\sum |x-x_i|\)

    容易发现 \(x\) 的正负会被抵消,剩下的部分可以用贡献法来计算。我们考虑 \(x_i\) 的贡献,设排名在 \(i\) 左边的选取了 \(a\) 个,排名在 \(i\) 右边的选取了 \(b\) 个。那么如果 \(a<b\) 贡献 \(x_i\)\(a=b\) 贡献 \(0\)\(a>b\) 贡献 \(-x_i\)

    对应的方案数大概是 \(\sum_{a}\sum_{b}{l\choose a}\cdot {r\choose b}={l\choose l-a}\cdot {r\choose b}\),我们利用组合意义就可以将它降维(枚举两边的个数和),设 \(j=i-1-a+b\),那么 \(x_i\) 的贡献就是:

    \[x_i\cdot(\sum_{j=i}^{n-1}{n-1\choose j}-\sum_{j=0}^{i-2}{n-1\choose j}) \]

    \(f_i=\sum_{j=i}^{n-1}{n-1\choose j}-\sum_{j=0}^{i-2}{n-1\choose j}\),这东西可以 \(O(n)\) 预处理出来,然后枚举质数可以做到 \(O(n\log w)\)

    更好的做法是对于每种质数,用埃氏筛统计出 \(p^x\) 的倍数个数,那么质数幂次相同的数可以一次计算,把 \(f_i\) 做前缀和就好做了,可以做到 \(O(n+w\log \log w)\)

    #include <cstdio>
    const int M = 300005;
    #define int long long
    const int MOD = 1e9+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans,fac[M],inv[M],w[M],a[M],c[M],f[M],vis[M];
    int C(int n,int m)
    {
    	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
    }
    void init(int n)
    {
    	fac[0]=inv[0]=inv[1]=1;
    	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
    	for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
    	c[0]=1;
    	for(int i=1;i<n;i++) c[i]=(c[i-1]+C(n-1,i))%MOD;
    	for(int i=2;i<=n;i++) f[i]=(f[i-1]+c[n-1]-c[i-1]-c[i-2])%MOD;
    }
    signed main()
    {
        n=read();m=300000;init(n);
    	for(int i=1;i<=n;i++) w[read()]++;
    	for(int i=2;i<=m;i++) if(!vis[i])
    	{
    		for(int j=i;j<=m;j+=i) vis[j]=1;
    		int j=1,k=1,l=0;
    		for(;i*k<=m;j++)
    		{
    			for(a[j]=0,k*=i,l=k;l<=m;l+=k) a[j]+=w[l];
    			if(!a[j]) break;
    		}
    		for(a[j]=0;--j;)
    			ans=(ans+j*(f[a[j]]-f[a[j+1]]))%MOD;
    	}
    	printf("%lld\n",(ans+MOD)%MOD);
    }
    

    New Year and the Tricolore Recreation

    题目描述

    点此看题

    注意两人操作时都不能只移动中间的棋子。

    解法

    首先考虑题目给了我们 \(n\) 个独立的子游戏,那么如果我们可以分别求出每个游戏的 \(\tt sg\) 函数就可以解决本题。

    考虑 \(\tt Alice\) 如果只移动最左边的棋子,相当于把左边的距离减少 \(d\),如果她移动左边和中间的棋子,相当于把右边的距离减少 \(d\)\(\tt Bob\) 的操作同理。这说明本题其实就是一个公平博弈游戏,每个子游戏等价于有两堆石子 \(w-b-1\)\(r-w-1\)

    我们只需要求出 \(sg[i]\) 表示大小为 \(i\) 的石子堆的 \(\tt sg\) 函数值,暴力跑是 \(O(n^2)\) 的。这东西竟然可以用 \(\tt bitset\) 优化,我们对于每个 \(\tt sg\) 值都维护一个 \(\tt bitset\),第 \(i\) 位就表示这个点 存在\(/\)不存在 这个 \(\tt sg\) 值的后继状态。

    那么我们从小到大计算 \(\tt sg\) 值,每次把这个点作为后继或到 \(sg[i]\)\(\tt bitset\) 上面去,可以预处理出步数的 \(\tt bitset\) 记为 \(z\)(由质数和两个质数的乘积构成),那么或上 z<<i 即可。

    但是这个做法依赖于性质:\(\forall sg[i]\leq 100\)(打表发现,感性理解就是转移较为稀疏),时间复杂度 \(O(\frac{n^2}{w})\)

    #include <cstdio>
    #include <bitset>
    using namespace std;
    const int M = 200005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans,cnt,vis[M],p[M],dp[M];
    bitset<M> b[105],z;
    void init(int n)
    {
    	for(int i=2;i<=n;i++)
    	{
    		if(!vis[i]) p[++cnt]=i,z[i]=1;
    		for(int j=1;j<=cnt && i*p[j]<=n;j++)
    		{
    			vis[i*p[j]]=1;
    			if(i%p[j]==0) break;
    		}
    	}
    	for(int i=1;i<=cnt;i++)
    		for(int j=i;j<=cnt;j++)
    		{
    			if(1ll*p[i]*p[j]>n) break;
    			z[p[i]*p[j]]=1;
    		}
    }
    signed main()
    {
    	n=read();m=read();init(2e5);
    	z[m]=0;m=200000;b[0]=z;
    	for(int i=1;i<=m;i++)
    	{
    		while(b[dp[i]][i]) dp[i]++;
    		b[dp[i]]|=(z<<i);
    	}
    	for(int i=1;i<=n;i++)
    	{
    		int a=read(),b=read(),c=read();
    		ans^=dp[b-a-1];
    		ans^=dp[c-b-1]; 
    	}
    	if(ans) puts("Alice\nBob");
    	else puts("Bob\nAlice");
    }
    
  • 相关阅读:
    我的WCF之旅(1):创建一个简单的WCF程序
    网页设计中颜色的搭配
    CSS HACK:全面兼容IE6/IE7/IE8/FF的CSS HACK
    UVa 1326 Jurassic Remains
    UVa 10340 All in All
    UVa 673 Parentheses Balance
    UVa 442 Matrix Chain Multiplication
    UVa 10970 Big Chocolate
    UVa 679 Dropping Balls
    UVa 133 The Dole Queue
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16270722.html
Copyright © 2020-2023  润新知