Wannafly挑战赛24

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37517391/article/details/83211347

Wannafly挑战赛24

题目连接

https://www.nowcoder.com/acm/contest/186#question


A.石子游戏

题解

注意到当石子个数为偶数的时候,每回合都会减少一堆偶数石子,因此,先手必胜.

我们可以不考虑奇数堆石子,因为必胜方始终可以动偶数堆.

当必败方将奇数堆分成一堆偶数和一堆奇数的时候,必胜方将新生成偶数堆移动到原有的偶数堆中即可抵消对方的移动.

代码

#include <iostream>
int main() {
    int n;
    std::cin >> n;
    int cnt = 0;
    for(int i = 1;i <= n;++i) {
        int tmp;
        std::cin >> tmp;
        if(tmp % 2 == 0) cnt ++;
    }
    if(cnt && cnt % 2 == 0) puts("Alice");
    else puts("Bob");
}

B.222333

题解

先暴力枚举 m + n m+n ,然后从小到大枚举 m m ,找到第一个适合的break即可.

代码

#include <iostream>

typedef long long LL;
LL P;
LL mypow(LL x,int n,LL P) {
    LL res = 1;
    while(n) {
        if(n & 1) res = res * x % P;
        x = x*x % P;
        n >>= 1;
    }
    return res;
}

int find(int s) {
    long long ans = 1;
    for(int x = 1;x < s;++x) {
        ans = (mypow(2,x,P) * mypow(3,s-x,P) % P + P-1)%P;
        if(ans == 0) return x;
    }
    return -1;
}

int main() {
    while(std::cin >> P) {
        for(int i = 2;i <= P;++i) {
            int x = find(i);
            if(x != -1){
                std::cout << x << " " << i-x << std::endl;
                break;
            }
        }
    }
    
}

C.失衡天平

题解

经典的动态规划问题.

我们记 d p [ i ] [ j ] dp[i][j] 表示考虑前 i i 个武器,取出来一些武器满足左边减去右边重量差为 j j ,所能取得的最大重量和.

递推方程:

d p [ i + 1 ] [ j w [ i + 1 ] ] d p [ i ] [ j ] + w [ i + 1 ] dp[i+1][j-w[i+1]] \leftarrow dp[i][j] + w[i+1]

d p [ i + 1 ] [ j + w [ i + 1 ] ] d p [ i ] [ j ] + w [ i + 1 ] dp[i+1][j+w[i+1]] \leftarrow dp[i][j] + w[i+1]

注意数组元素不能有负,因此需要给第二维一个 b a s e base .

代码

int n,m;
int w[107];
int dp[107][20010];
const int base = 10000;
int main() {
    std::ios::sync_with_stdio(false);
    std::cin >> n >> m;
    for(int i = 1;i <= n;++i)
        std::cin >> w[i];
    for(int i = 0;i <= 100;++i) for(int j = 0;j <= 20000;++j)
        dp[i][j] = -100000;
    
    dp[0][base] = 0;
    for(int i = 0;i < n;++i) {
        for(int j = 0;j <= 20000;++j) {
            if(j < 0) continue;
            dp[i+1][j] = std::max(dp[i][j],dp[i+1][j]);
            if(j + w[i+1] <= 20000)
                dp[i+1][j+w[i+1]] = std::max(dp[i+1][j+w[i+1]],dp[i][j] + w[i+1]);
            if(j - w[i+1] >= 0)
                dp[i+1][j-w[i+1]] = std::max(dp[i+1][j-w[i+1]],dp[i][j] + w[i+1]);
        }
    }
    int ans = 0;
    for(int i = 0;i <= m;++i) {
        ans = std::max(ans,dp[n][base+i]);
        ans = std::max(ans,dp[n][base-i]);
    }
    std::cout << ans << std::endl;
}

D.无限手套

题解

一眼动态规划.

d p [ i ] [ j ] dp[i][j] 表示考虑前 i i 种宝石,已经使用了 j j 颗宝石,所获得的可能的力量之和.

递推方程:

d p [ i + 1 ] [ j ] = t = 0 j d p [ i ] [ t ] ( a i + 1 ( j t ) 2 + b i + 1 ( j t ) + 1 ) dp[i+1][j] = \sum_{t = 0}^{j}{dp[i][t](a_{i+1}(j-t)^2+b_{i+1}(j-t)+1)}

将后面的部分展开

d p [ i + 1 ] [ j ] = t = 0 j ( a i + 1 d p [ i ] [ t ] t 2 ( b i + 1 + 2 j a i + 1 ) d p [ i ] [ t ] t + ( a i + 1 j 2 + b i + 1 j + 1 ) d p [ i ] [ t ] ) dp[i+1][j] = \sum_{t=0}^{j}(a_{i+1}*dp[i][t]*t^2 -(b_{i+1}+2j*a_{i+1})*dp[i][t]*t + (a_{i+1}*j^2+b_{i+1}*j+1)*dp[i][t])

继续化简得到

d p [ i + 1 ] [ j ] = a i + 1 t = 0 j d p [ i ] [ t ] t 2 ( b i + 1 + 2 j a i + 1 ) t = 0 j d p [ i ] [ t ] t + ( a i + 1 j 2 + b i + 1 j + 1 ) t = 0 j d p [ i ] [ t ] dp[i+1][j] = a_{i+1}\sum_{t=0}^{j}dp[i][t]t^2 -(b_{i+1}+2j*a_{i+1})\sum_{t=0}^{j}dp[i][t]t + (a_{i+1}*j^2+b_{i+1}*j+1)\sum_{t=0}^{j}dp[i][t]

如果我们令

s u m 2 [ j ] = t = 0 j d p [ i ] [ t ] t 2 sum_2[j] = \sum_{t=0}^{j}dp[i][t]t^2
s u m 1 [ j ] = t = 0 j d p [ i ] [ t ] t sum_1[j] = \sum_{t=0}^{j}dp[i][t]t
s u m 0 [ j ] = t = 0 j d p [ i ] [ t ] sum_0[j] = \sum_{t=0}^{j}dp[i][t]

那么

d p [ i + 1 ] [ j ] = a i + 1 s u m 2 [ j ] ( b i + 1 + 2 j a i + 1 ) s u m 1 [ j ] + ( a i + 1 j 2 + b i + 1 j + 1 ) s u m 0 [ j ] dp[i+1][j] = a_{i+1}sum_2[j] -(b_{i+1}+2j*a_{i+1})sum_1[j]+ (a_{i+1}*j^2+b_{i+1}*j+1)sum_0[j]

转移就变成 O ( 1 ) O(1) 的了,空间上再滚动数组优化一下就过了.

代码

typedef long long LL;
const int N = 10007;
const LL P = 998244353;
LL dp[2][N],a[N],b[N],sum2[2][N],sum1[2][N],sum0[2][N];
LL Mul(LL a,LL b) {
	return a * b % P;
}
LL Add(LL a,LL b) {
	return (a + b) % P;
}
int n,q;
int main() {
	std::ios::sync_with_stdio(false);
	std::cin >> n;
	rep(i,1,n) {
		std::cin >> a[i] >> b[i];
	}
	sum0[1][0] = 1;
	rep(i,1,10000) {
		dp[1][i] = ((a[1]*i%P*i%P) + (b[1]*i%P) + 1)%P;
		sum2[1][i] = Add(sum2[1][i-1],dp[1][i]*i%P*i%P);
		sum1[1][i] = Add(sum1[1][i-1],dp[1][i]*i%P);
		sum0[1][i] = Add(sum0[1][i-1],dp[1][i]);
	}
	rep(i,2,n) {
		rep(j,0,10000) {
			dp[i&1][j] = (Mul(a[i],sum2[(i+1)&1][j]) - Mul(b[i]+2*j*a[i]%P,sum1[(i+1)&1][j]) 
					+ Mul((a[i]*j%P*j%P+b[i]*j+1)%P,sum0[(i+1)&1][j]) + P )% P;

			sum0[i&1][j] = dp[i&1][j];
			sum1[i&1][j] = dp[i&1][j]*j%P;
			sum2[i&1][j] = dp[i&1][j]*j%P*j%P;
			if(j) {
				sum0[i&1][j] = Add(sum0[i&1][j],sum0[i&1][j-1]);
				sum1[i&1][j] = Add(sum1[i&1][j],sum1[i&1][j-1]);
				sum2[i&1][j] = Add(sum2[i&1][j],sum2[i&1][j-1]);
			}
		}
		memset(sum0[(i+1)&1],0,sizeof(sum0[(i+1)&1]));
		memset(sum1[(i+1)&1],0,sizeof(sum1[(i+1)&1]));
		memset(sum2[(i+1)&1],0,sizeof(sum2[(i+1)&1]));
	}
	std::cin >> q;
	while(q--){
		int x;
		std::cin >> x;
		std::cout << dp[n&1][x] << std::endl;
	}
	return 0;
}

E.旅行

题解

还没看…

F. wyf的超级多项式

题解

很棒的一道题,学了很多知识.

我们考虑 F n F_n 的递推公式,猜测

F n = c 1 F n 1 + c 2 F n 2 + . . . + c k F n k F_n = c_1F_{n-1}+c_2F_{n-2}+...+c_kF_{n-k}

下面我们需要求出 c 1 , c 2 , . . . , c k . c_1,c_2,...,c_k.

c 0 = 1 c_0 = -1 ,我们将递推式整理一下得到:

c 0 F n + c 1 F n 1 + c 2 F n 2 + . . . + c k F n k = 0 c_0F_n + c_1F_{n-1}+c_2F_{n-2}+...+c_kF_{n-k} = 0

由于前 F i F_{i} 的通项公式已经给出了,我们可以将其代入得到:

[ c 0 a 1 v 1 n c 0 a 2 v 2 n c 0 a 3 v 3 n . . . c 0 a k v k n c 1 a 1 v 1 n 1 c 1 a 2 v 2 n 1 c 1 a 3 v 3 n 1 . . . c 1 a k v k n 1 c 2 a 1 v 1 n 2 c 2 a 2 v 2 n 2 c 2 a 3 v 3 n 2 . . . c 2 a k v k n 2 . . . . . . . . . . . . . . . c k a 1 v 1 n k c k a 2 v 2 n k c k a 3 v 3 n k . . . c k a k v k n k ] \left[ \begin{matrix} c_0a_1v_1^n &amp; c_0a_2v_2^n &amp; c_0a_3v_3^n &amp; ...&amp; c_0a_kv_k^n\\ c_1a_1v_1^{n-1} &amp; c_1a_2v_2^{n-1} &amp; c_1a_3v_3^{n-1} &amp; ...&amp; c_1a_kv_k^{n-1}\\ c_2a_1v_1^{n-2} &amp; c_2a_2v_2^{n-2} &amp; c_2a_3v_3^{n-2} &amp; ...&amp; c_2a_kv_k^{n-2}\\ ... &amp; ... &amp; ... &amp; ... &amp; ...\\ c_ka_1v_1^{n-k} &amp; c_ka_2v_2^{n-k} &amp; c_ka_3v_3^{n-k} &amp; ...&amp; c_ka_kv_k^{n-k}\\ \end{matrix} \right]
上面矩阵中所有的项之和等于 0 0 .

首先,分析一下这个矩阵,每一行的和肯定不能为 0 0 了,因为它使我们要求的答案,那么我们可以利用充分条件构造每一列都是 0 0 ,这样整个矩阵所有项的和就是 0 0 了.

这么构造是有原因的,每一列形式都很相似,可以归结到多项式中去.

G ( x ) = c k + c k 1 x + . . . + c 0 x k G(x) = c_k + c_{k-1}x + ... + c_0x^k ,显然 v 1 , v 2 . . . , v k v_1,v_2...,v_k G ( x ) G(x) k k 个零点,因此我们得到 G ( x ) = a ( x v 1 ) ( x v 2 ) . . . ( x v k ) G(x) = a(x-v_1)(x-v_2)...(x-v_k)

又由于 c 0 = 1 c_0 = -1 ,得到 a = 1 a = -1 ,

所以 G ( x ) = ( x v 1 ) ( x v 2 ) . . . ( x v k ) G(x) = -(x-v_1)(x-v_2)...(x-v_k)

c c 序列即 G G 函数的系数,因此求出了 G G 就可以确定 c c 了.

对于这种形式的多项式展开,我们使用分治 F F T / N T T FFT/NTT 就可以在 O ( n l o g n 2 ) O(nlogn^2) 时间内做到了.

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
#define clr(x) memset(x,0,sizeof(x))
#define setinf(x) memset(x,0x3f,sizeof(x))

typedef long long LL;
const int N = 1 << 20;
const int P = 1004535809;
const int G = 3;
const int NUM = 20;

LL  wn[NUM];
LL  a[N], b[N];

LL quick_mod(LL a, LL b, LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % m;
            b--;
        }
        b >>= 1;
        a = a * a % m;
    }
    return ans;
}

void GetWn()
{
    for(int i = 0; i < NUM; i++)
    {
        int t = 1 << i;
        wn[i] = quick_mod(G, (P - 1) / t, P);
    }
}
void Rader(LL a[], int len)
{
    int j = len >> 1;
    for(int i = 1; i < len - 1; i++)
    {
        if(i < j) std::swap(a[i], a[j]);
        int k = len >> 1;
        while(j >= k)
        {
            j -= k;
            k >>= 1;
        }
        if(j < k) j += k;
    }
}

void NTT(LL a[], int len, int on)
{
    Rader(a, len);
    int id = 0;
    for(int h = 2; h <= len; h <<= 1)
    {
        id++;
        for(int j = 0; j < len; j += h)
        {
            LL w = 1;
            for(int k = j; k < j + h / 2; k++)
            {
                LL u = a[k] % P;
                LL t = w * a[k + h / 2] % P;
                a[k] = (u + t) % P;
                a[k + h / 2] = (u - t + P) % P;
                w = w * wn[id] % P;
            }
        }
    }
    if(on == -1)
    {
        for(int i = 1; i < len / 2; i++)
            std::swap(a[i], a[len - i]);
        LL inv = quick_mod(len, P - 2, P);
        for(int i = 0; i < len; i++)
            a[i] = a[i] * inv % P;
    }
}

void Conv(LL a[], LL b[], int n)
{
    NTT(a, n, 1);
    NTT(b, n, 1);
    for(int i = 0; i < n; i++)
        a[i] = a[i] * b[i] % P;
    NTT(a, n, -1);
}
LL v[N],F[N];

LL C[N];

inline int expand(int x){
	int res = 1;
	while(res < x) res <<= 1;
	res <<= 1;
	return res;
}

void solve(int l,int r,LL Ans[]) {
	if(r == l) {
		Ans[0] = P-v[l];
		Ans[1] = 1;
		return ;
	}
	int mid = (l + r) / 2;
	int lft = mid - l + 1;
	int rgt = r - mid;
	LL *LA = new LL[(lft+1)*2],*RA = new LL[(rgt+1)*2];
	solve(l,mid,LA);
	solve(mid+1,r,RA);
	rep(i,0,lft) a[i] = LA[i];
	rep(i,0,rgt) b[i] = RA[i];
	int len = 1;
	while(len <= r-l+1) len <<= 1;
	rep(i,lft+1,len) a[i] = 0;
	rep(i,rgt+1,len) b[i] = 0;
	NTT(a,len,1);
	NTT(b,len,1);
	rep(i,0,len) a[i] = a[i] * b[i];
	NTT(a,len,-1);
	rep(i,0,r-l+1) Ans[i] = a[i];
}
int n,k;
int main()
{
    GetWn();
	std::ios::sync_with_stdio(false);
	std::cin >> n >> k;
	rep(i,1,k) {
		std::cin >> v[i];
	}
	rep(i,1,k) {
		std::cin >> F[i];
	}

	solve(1,k,C);
	
	rep(i,k+1,n) {
		rep(j,1,k) {
			F[i] = (F[i] + ((P-C[k-j] % P)*F[i-j] % P)) % P;
		}
	}
	std::cout << F[n] << std::endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_37517391/article/details/83211347