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
題解
先暴力枚舉,然後從小到大枚舉,找到第一個適合的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.失衡天平
題解
經典的動態規劃問題.
我們記表示考慮前個武器,取出來一些武器滿足左邊減去右邊重量差爲,所能取得的最大重量和.
遞推方程:
注意數組元素不能有負,因此需要給第二維一個.
代碼
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.無限手套
題解
一眼動態規劃.
表示考慮前種寶石,已經使用了顆寶石,所獲得的可能的力量之和.
遞推方程:
將後面的部分展開
繼續化簡得到
如果我們令
那麼
轉移就變成的了,空間上再滾動數組優化一下就過了.
代碼
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的超級多項式
題解
很棒的一道題,學了很多知識.
我們考慮的遞推公式,猜測
下面我們需要求出
令,我們將遞推式整理一下得到:
由於前的通項公式已經給出了,我們可以將其代入得到:
上面矩陣中所有的項之和等於.
首先,分析一下這個矩陣,每一行的和肯定不能爲了,因爲它使我們要求的答案,那麼我們可以利用充分條件構造每一列都是,這樣整個矩陣所有項的和就是了.
這麼構造是有原因的,每一列形式都很相似,可以歸結到多項式中去.
記,顯然是的個零點,因此我們得到
又由於,得到,
所以
序列即函數的係數,因此求出了就可以確定了.
對於這種形式的多項式展開,我們使用分治就可以在時間內做到了.
代碼
#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;
}