【题目记录】——The 2021 ICPC Asia Jinan Regional Contest


题目集地址 The 2021 ICPC Asia Jinan Regional Contest
我们只做了K题签到题。。

思维:Optimal Strategy,Insidemen,Permutation Pair
数学:Arithmetic Sequence,Determinant,Strange Series
动态规划:Game Coin
搜索:Search For Mafuyu
2-SAT:Coloring Rectangles

C Optimal Strategy 组合数

题目大意:有 n 件物品,第 i 件的价值为 a[i]。A 和 B 轮流取物品,A 先手。每个玩家都要最大化自己取到的物品的价值和,求有多少种可能的游戏过程。
思路:参考2021icpc济南题解
在偶数情况下,只要有一个人选了当前最大的数,那么后面那个人就一定要跟着选一个一样大的 。不然一定会亏。
比如说:1 1 2 2 3 3,如果先手选了3,后手一定会选3。
所以在排列中最大的一定是成对在一起的。
当我们把排列中最大的数删掉之后第二大的就一定会成对,删掉第二大的之后第三大的就一定会成对…
所以我们可以从最小的开始考虑,先把最小的放好,再把第二小的成对插进去,这就是挡板法了,不难发现这一步等价于球不同,盒子不同,有空盒的球盒模型。依次推到下去我们可以得到数字全部都是偶数的推导公式。
假设cnt是当前已经放置的球的个数,当前要放置的第i个球,两两成对共有t=(c[i]/2)(向下取整)对。对应于球放盒的问题中,可以放置的位置就是盒子,有cnt+1个盒子,盒子不同,有t个球,球是相同的,盒子可以为空,利用挡板法,可知对应的组合数的公式为 C t + c n t c n t C_{t+cnt}^{cnt} Ct+cntcnt,因为球与球之间是不同的,所以再乘一个球的全排列, A c [ i ] c [ i ] A_{c[i]}^{c[i]} Ac[i]c[i]

存在奇数个时就会存在单独的数字,我拿了之后我必然比你多一个。这样可以得出结论当存在单独的数的时候优先拿走最大单个数。
放在排列中,单个的10必然是第一个因为它比所有的数字都大,而同样的单个的8一定会在1和4前面。
这体现了一个什么问题?单个对排列个数没卵用,因为我们是把大的往小的插空,单个的放在最前面就好了(但是记得乘排列的时候不能省略这一位)。
最后得到的公式就是这样的
∏ i = 1 n c [ i ] ! C ∑ j = 1 i − 1 c [ j ] + ⌊ c [ i ] / 2 ⌋ ⌊ c [ i ] / 2 ⌋ \prod_{i=1}^nc[i]!C_{\sum_{j=1}^{i-1}c[j]+\lfloor{c[i]/2}\rfloor}^{\lfloor{c[i]/2}\rfloor} i=1nc[i]!Cj=1i1c[j]+c[i]/2c[i]/2

AC代码:

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int mod=998244353;
const int N=1e6+6;
int c[N];
ll pow(ll a, ll n, ll p)    //快速幂 a^n % p
{
    ll ans = 1;
    while(n)
    {
        if(n & 1) ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}

ll niyuan(ll a, ll p)   //费马小定理求逆元
{
    return pow(a, p - 2, p);
}

ll A(int n,int m)//A_n^m
{
    ll res = 1;
    for(int i = n;i>=n-m+1;i--)
    {
        res=(res*i)%mod;
    }
    return res;
}
ll C(int n,int m)//C_n^m
{
    if(n==0||m==0)
    {
        return 1;
    }

    return A(n,m)*niyuan(A(m,m),mod)%mod;
}
void solve()
{
    int n;
    scanf("%d",&n);
    int cnt=0;
    for(int i = 1;i <= n;i++)
    {
        int t;
        scanf("%d",&t);
        c[t]++;
    }
    ll ans=1;
    for(int i = 1;i <= n;i++)
    {
        int t = c[i]/2;
        if(t){
            ans = (ans*C(cnt+t,t)%mod*A(c[i],c[i]))%mod;
        }
        cnt+=c[i];
    }
    printf("%lld\n",ans);
}

int main()
{
	int t = 1;
	while(t--)
	{
		solve();
	}
    return 0;
}

H Game Coin

题目大意:Bob第i天需要a[i]个游戏金币来满足自己的游戏购物欲望。获取金币有两种方法,第一种方法是每个金币可以直接花费t元购买,第二种方法是购买游戏商城内部的金币卡来每天领取临时金币,总共有n种金币卡,第j种金币卡有一个购买价格c[j],使用期限d[j]和每天的金币领取数量w[j]。临时金币只能当天使用,第二天就失效。
倘若第i天花费c[j]元购买了第j种金币卡,那么从第i 天到第i+ d[j]-1天每天都可以用该金币卡来领取w[i]个临时金币。每种金币卡可以无限数量购买,但是每次购买新的卡会把手上已有的卡给覆盖掉,保证手上最多只有一张卡。(例如,第c 天买了一张金币卡i,可以用来领取w[i]个临时金币,然后购买一张金币卡j(这时会覆盖掉金币卡i),可以继续用新卡来领取w[j]个金币,通过这种方式第太天可以获得w[i]+ w[j]个金币
计算满足Bob m天的游戏购物欲望最少需要花多少钱。m ≤100000, n≤400, ∑ a [ i ] ≤ 500000 \sum a[i]≤500000 a[i]500000
c [ j ] , d [ j ] , w [ j ] ∈ [ 0 , 109 ] c[j], d[j], w[j] \in [0,109] c[j],d[j],w[j][0,109]
思路:

K Search For Mafuyu 欧拉序列

题目大意:有n个房间,(n-1)对是连通的,M等概率隐藏在S的各个非1号房间中.现在K从1号房间出发,要找到M,K可以在连通的房间中移动,移动一步耗时一秒。对于每组样例,输出最小的期望时间.
思路:就是欧拉序列,首先有一个规律就是结果与你遍历的顺序是无关的,我们按照欧拉序遍历所有的点,记录每一个点第一次到达的时间,求和在除以n-1就可以了。

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
struct edge{
    int to;
    int next;
}E[202];
int head[101],cnt,cntt;
int f1[101];
void add(int from, int to)
{
    cnt++;
    E[cnt].to = to;
    E[cnt].next = head[from];
    head[from] = cnt;
}
void dfs(int u,int fa)
{
    for(int i = head[u];i;i = E[i].next)
    {
        int v = E[i].to;
        if(v==fa)continue;
        cntt++;
        f1[v]=cntt;
        dfs(v,u);
        cntt++;
    }
}
void solve()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n-1;i++){
        int from,to;
        scanf("%d%d",&from,&to);
        add(from,to);
        add(to,from);
    }
    dfs(1,1);
    ll res = 0;
    for(int i = 1;i <= n;i++)
    {
        res+=f1[i];
    }
    double ans = res*1.0/(n-1);
    printf("%.10lf\n",ans);
}

int main()
{
//	freopen("in.txt","r",stdin);
	int t;
	scanf("%d",&t);
	while(t--)
	{
		solve();
		cnt=0;cntt=0;
		memset(head,0,sizeof(head));
		memset(E,0,sizeof(E));
	}
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值