IOI2020集训队作业-23 (CF674F,ARC091F)

B - CF674F Bears and Juice

Sol

最后我们得到的信息是:每头熊有没有睡觉,以及如果睡觉了是在哪一天睡的。我们可能得到的信息的种数显然是桶数量的一个上界。假设总共有 d d d天,信息种数就是:

∑ i = 0 min ⁡ { p , n − 1 } d i ( n i ) \sum_{i=0}^{\min\{p,n-1\}} d^i {n\choose i} i=0min{p,n1}di(in)

其中 i i i枚举的是睡觉了的熊的数量, i i i不能大于 p p p i i i不能等于 n n n

这个上界是可以达到的:我们令每一桶酒对应一种情况;在第 i i i天,让第 j j j头熊喝所有满足下面条件的酒 x x x:在 x x x对应的情况中,第 j j j头熊是第 i i i天睡觉的。最后达到的情况恰好就是有酒的桶对应的情况。

所以答案就是可能得到的信息的种数。由于 p p p很小可以直接暴力算出 ( n i ) {n\choose i} (in):用 1 , 2 , ⋯ i 1,2,\cdots i 1,2,i去对 n ( n − 1 ) ( n − 2 ) ⋯ ( n − i + 1 ) n(n-1)(n-2)\cdots (n-i+1) n(n1)(n2)(ni+1)进行约分,复杂度是 O ( i 2 log ⁡ i ) O(i^2\log i) O(i2logi)。然后再 O ( p q ) O(pq) O(pq)算出答案。总复杂度 O ( p 3 log ⁡ p + p q ) O(p^3\log p + pq) O(p3logp+pq)

Code

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define PB push_back
#define MP make_pair
#define FIR first
#define SEC second
#define ll long long
#define uint unsigned
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
int gcd(int a,int b) { return b?gcd(b,a%b):a; }
uint calc_C(int n,int m) {
    vector<uint> w;
    for(int i=n;i>n-m;--i) w.PB(i);
    for(int i=1;i<=m;++i) {
        int t=i;
        for(int j=0;t!=1&&j<w.size();++j) if(w[j]!=1) {
           int g=gcd(w[j],t);
           w[j]/=g,t/=g;
        }
    }
    uint ans=1;
    for(int j=0;j<w.size();++j) ans*=w[j];
    return ans;
}
int n,p,q;
uint C[150];
uint getans(int d) {
    uint ans=0,t=1;
    for(int j=0;j<=p;++j)
        ans+=C[j]*t,t*=d;
    return ans;
}
int main() {
    rd(n),rd(p),rd(q); p=min(p,n-1);
    for(int i=0;i<=p;++i) C[i]=calc_C(n,i);
    uint ans=0;
    for(int i=1;i<=q;++i) ans^=i*getans(i);
    printf("%u",ans);
    return 0;
}

C - ARC091F Strange Nim

Sol

只需要对每一堆算出它的Grundy number就可以了。

通过找规律会发现,设 g k ( n ) g_k(n) gk(n)表示 K i = k K_i = k Ki=k,堆中石子的数量为 n n n的Grundy number,则

g k ( n ) = { 0 0 ≤ n < k n k k ∣ n g k ( n − ⌊ n k ⌋ − 1 ) k ∤ n g_k(n) = \begin{cases} 0 & 0\le n < k\\ {n\over k} & k \mid n \\ g_k(n-\lfloor{n\over k}\rfloor - 1) & k\nmid n \end{cases} gk(n)=0kngk(nkn1)0n<kknkn

T = max ⁡ { a i } T=\sqrt {\max\{ a_i\}} T=max{ai} ,如果 ⌊ n k ⌋ > T \lfloor {n\over k} \rfloor > T kn>T,直接递归算 g k ( n − ⌊ n k ⌋ − 1 ) g_k(n-\lfloor {n\over k}\rfloor-1) gk(nkn1);否则,跳到第一个为 k k k的倍数的或者 ⌊ n k ⌋ \lfloor{n\over k}\rfloor kn变化了的 n ′ = n − t ( ⌊ n k ⌋ + 1 ) n'= n-t(\lfloor{n\over k}\rfloor + 1) n=nt(kn+1)。前一部分中至多跳 n T n\over T Tn次;后一部分中每跳一次 ⌊ n k ⌋ \lfloor{n\over k}\rfloor kn的值都会改变,所以后一部分至多跳 T T T次。所以总时间复杂度 O ( ∑ a i ) O(\sum \sqrt {a_i}) O(ai )

Code

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define PB push_back
#define MP make_pair
#define FIR first
#define SEC second
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int T=30000;
int calc(int n,int k) {
    if(n<k) return 0;
    if(n%k==0) return n/k;
    int t=(n/k+1);
    if(t>T) return calc(n-t,k);
    int x=(n%k)/t;
    if((n-x*t)%k) x++;
    return calc(n-x*t,k);
}
int n,ans;
int main() {
    rd(n);
    for(int i=1;i<=n;++i) {
        int a,k; rd(a),rd(k);
        ans^=calc(a,k);
    }
    if(ans) printf("Takahashi");
    else printf("Aoki");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值