abc204
D - Cooking 832
物品分两组,使重量差最小
经典背包,容量为所有物品重量和的一半,最大化重量
E - Rush Hour 2 1710
给定图,每条边 \(u\to v\) 有参数 \(C,D\),若在 \(t\) 时刻从 \(u\) 出发,则能在 \(t+C+\lfloor \frac{D}{t+1} \rfloor\) 时刻到达 \(v\)。可以在任意结点停留任意整数秒,问从 \(1\) 出发最早何时能走到 \(n\)。
由基本不等式,大概在 \(\sqrt D-1\) 附近取最小值,如果没到这个时间就等一会儿。跑Dijkstra,维护到集合外的点的最早时刻
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll get(ll t, int c, int d) { // t时刻到达此边的起点端,最快何时能到达另一端
ll ans = t + c + d / (t + 1);
for (int i = 0; i <= 1; i++) { //不知道具体是哪个,附近的都试试吧!
ll j = (int)sqrt(d) - 1 + i;
if (j >= t) ans = min(ans, j + c + d / (j + 1));
}
return ans;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n, m;
cin >> n >> m;
vector<vector<array<int, 3>>> g;
g.resize(n + 1);
while (m--) {
int a, b, c, d;
cin >> a >> b >> c >> d;
g[a].push_back({b, c, d});
g[b].push_back({a, c, d});
}
priority_queue<pair<ll, int>> heap; heap.push({0, 1});
vector<ll> dis(n + 1, -1); dis[1] = 0;
vector<bool> vis(n + 1);
while (heap.size()) {
int u = heap.top().second; heap.pop();
if (vis[u]) continue; vis[u] = true;
for (auto [v, c, d] : g[u]) {
if (vis[v]) continue;
ll t = get(dis[u], c, d);
if (dis[v] == -1 || t < dis[v])
dis[v] = t, heap.push({-t, v});
}
}
cout << dis[n];
return 0;
}
F - Hanjo 2 2044
用 \(1*1,1*2,2*1\) 瓷砖把 \(H*W\) 矩形不重不漏地铺满,问方案数。\(H\le 6, W\le 1e12\)
一眼状压dp+矩阵快速幂。状压dp的部分类似“蒙德里安的梦想”。算出前100项然后bm线性递推,然后就过了!(如果wa了就多算几项,咳咳)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e2, M = (1 << 6), P = 998244353;
namespace linear_seq {
const int mod = P;
ll qmi(ll a, ll b) {
ll res = 1; a %= mod;
for(; b; b >>= 1) {
if(b&1) res = res * a % mod;
a = a * a % mod;
}
return res;
}
#define rep(i,a,n) for (int i=a;i<n;i++)
#define SZ(x) ((int)(x).size())
#define pb push_back
typedef vector<ll> VI;
const int N=10010;
ll res[N],base[N],_c[N],_md[N];
vector<int> Md;
void mul(ll *a,ll *b,int k) {
rep(i,0,k+k) _c[i]=0;
rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
for (int i=k+k-1;i>=k;i--) if (_c[i])
rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
rep(i,0,k) a[i]=_c[i];
}
int solve(ll n,VI a,VI b) { // a 系数 b 初值 b[n+1]=a[0]*b[n]+...
ll ans=0,pnt=0;
int k=SZ(a);
assert(SZ(a)==SZ(b));
rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
Md.clear();
rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
rep(i,0,k) res[i]=base[i]=0;
res[0]=1;
while ((1ll<<pnt)<=n) pnt++;
for (int p=pnt;p>=0;p--) {
mul(res,res,k);
if ((n>>p)&1) {
for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
}
}
rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
if (ans<0) ans+=mod;
return ans;
}
VI BM(VI s) {
VI C(1,1),B(1,1);
int L=0,m=1,b=1;
rep(n,0,SZ(s)) {
ll d=0;
rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
if (d==0) ++m;
else if (2*L<=n) {
VI T=C;
ll c=mod-d*qmi(b,mod-2)%mod;
while (SZ(C)<SZ(B)+m) C.pb(0);
rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
L=n+1-L; B=T; b=d; m=1;
} else {
ll c=mod-d*qmi(b,mod-2)%mod;
while (SZ(C)<SZ(B)+m) C.pb(0);
rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
++m;
}
}
return C;
}
int gao(VI a,ll n) {
VI c=BM(a);
c.erase(c.begin());
rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
}
};
int main() {
ll h, w, W;
cin >> h >> w;
W = w;
vector<ll> g(h + 1);
g[0] = g[1] = 1;
for (int i = 2; i <= h; i++)
g[i] = (g[i - 1] + g[i - 2]) % P;
w = min(w, 100ll);
vector<vector<ll>> f(w + 1, vector<ll>(1 << h));
f[0][0] = 1;
for (int i = 1; i <= w; i++) {
for (int a = 0; a < (1 << h); a++) {
for (int b = 0; b < (1 << h); b++) {
if (a & b) continue;
int res = 1;
for (int k = 0, st = a | b, t = 0; k <= h; k++) { //t为st中连续0的长度
if (k == h) res *= g[t];
else if (st >> k & 1) res *= g[t], t = 0;
else t++;
}
(f[i][b] += f[i - 1][a] * res % P) %= P;
}
}
}
vector<ll> tmp;
for (int i = 1; i <= w; i++)
tmp.push_back(f[i][0]);
cout << linear_seq::gao(tmp, W - 1);
}
当然正常人都是老老实实写矩阵快速幂:用 \(2^H\) 维行向量 \(V_i\) 表示第 \(i\) 行是每个状态时的方案数, 处理出转移矩阵即可
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int P = 998244353;
const int N = 1 << 6;
ll A[N][N], B[N][N];
void mul(ll a[N][N], ll b[N][N]) {
static ll c[N][N]; memset(c, 0, sizeof c);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
for (int k = 0; k < N; k++)
(c[i][j] += a[i][k] * b[k][j] % P) %= P;
memcpy(a, c, sizeof c);
}
void qmi(ll a[N][N], ll b) {
static ll s[N][N]; memset(s, 0, sizeof s);
for (int i = 0; i < N; i++) s[i][i] = 1;
for (; b; b >>= 1) {
if (b & 1) mul(s, a);
mul(a, a);
}
memcpy(a, s, sizeof s);
}
int main() {
ll h, w;
cin >> h >> w;
vector<ll> g(h + 1);
g[0] = g[1] = 1;
for (int i = 2; i <= h; i++)
g[i] = (g[i - 1] + g[i - 2]) % P;
for (int a = 0; a < (1 << h); a++) {
for (int b = 0; b < (1 << h); b++) {
if (a & b) continue;
B[a][b] = 1;
for (int k = 0, st = a | b, t = 0; k <= h; k++) { //t为st中连续0的长度
if (k == h) B[a][b] *= g[t];
else if (st >> k & 1) B[a][b] *= g[t], t = 0;
else t++;
}
}
}
A[0][0] = 1;
qmi(B, w);
mul(A, B);
cout << A[0][0];
return 0;
}
运行时间:bm线性递推2ms(第一次Fastest Code!),矩阵快速幂55ms
我的矩阵快速幂还老写错,哪个算法useless不用我说了吧(误