mns 1118

A

同步率竟然高达个位数 /kk。看来是没救了,这么简单的 dp 都没有想出来。

B

这有蓝?这有蓝?这有蓝?这有蓝?这有蓝?这有蓝?这有蓝?这有蓝?

前置知识

本文假设你有 小学二年级 高三水平,要求你知道:

  • 导数的概念与基本性质

题意

给你两个\(m\) 次多项式 \(F\)\(G\),每一次同时:

  • \(F \leftarrow G - G'\)
  • \(G \leftarrow F + F'\)

\(n\) 次,求做完之后的 \(F\)\(G\)

解法

众所周知,看到这种大数学题,我们应该 找 规 律

考虑从 \(F\) 开始变换:

\(F_0 = F\)

\(G_1 = F - F'\)

\(F_2 = G_1 + G_1' = F - F' + F' - F'' = F - F''\)

\(G_3 = F_2 - F_2' = F - F' - F'' + F'''\)

\(F_4 = G_3 + G_3' = F - F' - F'' + F''' + F' - F'' - F''' + F'''' = F - 2F'' + F''''\)

容易瞪出 \(F_{2n} =\)

\[\sum \limits_{i = 0}^n (-1)^i F^{(2i)} \binom{n}{i} \]

证明:

其中 \(F^{(n)}\)\(F\)\(n\) 阶导数 (如果你连这都不知道建议小学二年级重修)
考虑把 F 作为自变量而把求导的次数当作次数,这样我们就得到了一个新的多项式。如上所示,每两次操作就是乘上 \(1 - x^2\)。所以直接展开 \((1 - x^2)^n\) 就可以得到这个结论了。

实现得小心一点是可以做到 \(O(m^2)\) 的。

//#define DEBUG
#include <bits/stdc++.h>
#ifdef DEBUG
#include <windows.h>
#endif
#define int ll
#define ctz __builtin_ctzll
#define popc __builtin_popcountll
#define lowbit(x) ((x) & -(x))
#define ct1(x) ctz((x) + 1)
using namespace std;
using ll = long long;
const int kMod = int(1e9) + 7, kInf = 0x3f3f3f3f3f3f3f3f;
template<class T>T read(istream &is = cin) {
  T x;
  is >> x;
  return x;
}
int readInt(istream &is = cin) {return read<int>(is);}
template<typename T>
T qpow(T x, T y, T k = 0) {
  if (x == 0 && y == 0) return k;
  if (x == 0) return 0;
  x %= kMod;
  T res = 1;
  while(y) {
    if (y & 1) res = 1ll * res * x % kMod;
    x = 1ll * x * x % kMod;
    y >>= 1;
  }
  return res;
}
int qinv(int x) {return qpow(x, kMod - 2);}
random_device rd;
mt19937_64 mt(rd());

int n, m, inv[20020];
vector<int> f, g, ff, gg;

// 注意由于 n 可以特别大,这里是不能预处理组合数的,求组合数看下面
void precal() {
  for(int i = 1; i <= 20000; i++) inv[i] = qinv(i);
}
int C(int x, int y) {
  if(x < 0 || y < 0 || x < y) return 0;
  if(y == 0) return 1;
  return C(x - 1, y - 1) * x % kMod * inv[y] % kMod;
}

void daoguan(vector<int> &v) {
  for(int i = 0; i < int(v.size()) - 1; i++) {
    v[i] = v[i + 1] * (i + 1) % kMod;
  }
  v.back() = 0;
}
bool fin(vector<int> &v) {
  for(auto i : v) if(i) return 0;
  return 1;
}

// cnt = m / 2
vector<int> FtF(vector<int> F, int cnt) {
  int fl = F.size();
  vector<int> res;
  res.resize(fl);
  for(int i = 0; i <= cnt; i++) {
    if(fin(F)) break; // 求导求成 0 了就可以不做了,这样子最多导 m + 1 次
    // 此处如果不这样实现,可以被轻松卡成三次方级别导致 TLE
    int tx = C(cnt, i);
    for(int j = 0; j < fl; j++) {
      res[j] = (res[j] + kMod + (i % 2? -1 : 1) * tx * F[j] % kMod) % kMod;
    }
    daoguan(F);
    daoguan(F);
  }
  return res;
}
// 处理上面的流程无法处理的边界
vector<int> FtG(vector<int> F) {
  int fl = F.size();
  vector<int> res = F;
  daoguan(F);
  for(int i = 0; i < fl; i++) {
    res[i] = (res[i] + kMod - F[i]) % kMod;
  }
  return res;
}
vector<int> GtF(vector<int> G) {
  int gl = G.size();
  vector<int> res = G;
  daoguan(G);
  for(int i = 0; i < gl; i++) {
    res[i] = (res[i] + G[i]) % kMod;
  }
  return res;
}

signed main() {
#ifndef DEBUG
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
#endif
  precal();
  cin >> n >> m;
  f.resize(m + 1), g.resize(m + 1);
  for(int i = 0; i <= m; i++) cin >> f[i];
  for(int i = 0; i <= m; i++) cin >> g[i];
  f = FtF(f, n / 2);
  if(n % 2) gg = FtG(f);
  else ff = f;
  g = GtF(g); // 我懒得再推一遍了
  g = FtF(g, (n - 1) / 2);
  if((n - 1) % 2) gg = FtG(g);
  else ff = g;
  for(auto i : ff) cout << i << ' ';
  cout << '\n';
  for(auto i : gg) cout << i << ' ';
  cout << '\n';
  return 0;
}

C

场上没想出最优策略,场后看了题解醍醐灌顶,哎。

posted @ 2025-11-18 17:15  hhc0001  阅读(3)  评论(0)    收藏  举报