Wannafly挑戰賽24

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+nm+n,然後從小到大枚舉mm,找到第一個適合的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.失衡天平

題解

經典的動態規劃問題.

我們記dp[i][j]dp[i][j]表示考慮前ii個武器,取出來一些武器滿足左邊減去右邊重量差爲jj,所能取得的最大重量和.

遞推方程:

dp[i+1][jw[i+1]]dp[i][j]+w[i+1]dp[i+1][j-w[i+1]] \leftarrow dp[i][j] + w[i+1]

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

注意數組元素不能有負,因此需要給第二維一個basebase.

代碼

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.無限手套

題解

一眼動態規劃.

dp[i][j]dp[i][j]表示考慮前ii種寶石,已經使用了jj顆寶石,所獲得的可能的力量之和.

遞推方程:

dp[i+1][j]=t=0jdp[i][t](ai+1(jt)2+bi+1(jt)+1)dp[i+1][j] = \sum_{t = 0}^{j}{dp[i][t](a_{i+1}(j-t)^2+b_{i+1}(j-t)+1)}

將後面的部分展開

dp[i+1][j]=t=0j(ai+1dp[i][t]t2(bi+1+2jai+1)dp[i][t]t+(ai+1j2+bi+1j+1)dp[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])

繼續化簡得到

dp[i+1][j]=ai+1t=0jdp[i][t]t2(bi+1+2jai+1)t=0jdp[i][t]t+(ai+1j2+bi+1j+1)t=0jdp[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]

如果我們令

sum2[j]=t=0jdp[i][t]t2sum_2[j] = \sum_{t=0}^{j}dp[i][t]t^2
sum1[j]=t=0jdp[i][t]tsum_1[j] = \sum_{t=0}^{j}dp[i][t]t
sum0[j]=t=0jdp[i][t]sum_0[j] = \sum_{t=0}^{j}dp[i][t]

那麼

dp[i+1][j]=ai+1sum2[j](bi+1+2jai+1)sum1[j]+(ai+1j2+bi+1j+1)sum0[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的超級多項式

題解

很棒的一道題,學了很多知識.

我們考慮FnF_n的遞推公式,猜測

Fn=c1Fn1+c2Fn2+...+ckFnkF_n = c_1F_{n-1}+c_2F_{n-2}+...+c_kF_{n-k}

下面我們需要求出c1,c2,...,ck.c_1,c_2,...,c_k.

c0=1c_0 = -1,我們將遞推式整理一下得到:

c0Fn+c1Fn1+c2Fn2+...+ckFnk=0c_0F_n + c_1F_{n-1}+c_2F_{n-2}+...+c_kF_{n-k} = 0

由於前FiF_{i}的通項公式已經給出了,我們可以將其代入得到:

[c0a1v1nc0a2v2nc0a3v3n...c0akvknc1a1v1n1c1a2v2n1c1a3v3n1...c1akvkn1c2a1v1n2c2a2v2n2c2a3v3n2...c2akvkn2...............cka1v1nkcka2v2nkcka3v3nk...ckakvknk] \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]
上面矩陣中所有的項之和等於00.

首先,分析一下這個矩陣,每一行的和肯定不能爲00了,因爲它使我們要求的答案,那麼我們可以利用充分條件構造每一列都是00,這樣整個矩陣所有項的和就是00了.

這麼構造是有原因的,每一列形式都很相似,可以歸結到多項式中去.

G(x)=ck+ck1x+...+c0xkG(x) = c_k + c_{k-1}x + ... + c_0x^k,顯然v1,v2...,vkv_1,v_2...,v_kG(x)G(x)kk個零點,因此我們得到G(x)=a(xv1)(xv2)...(xvk)G(x) = a(x-v_1)(x-v_2)...(x-v_k)

又由於c0=1c_0 = -1,得到a=1a = -1,

所以G(x)=(xv1)(xv2)...(xvk)G(x) = -(x-v_1)(x-v_2)...(x-v_k)

cc序列即GG函數的係數,因此求出了GG就可以確定cc了.

對於這種形式的多項式展開,我們使用分治FFT/NTTFFT/NTT就可以在O(nlogn2)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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章