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不用我说了吧(误

posted @ 2023-10-26 11:06  Bellala  阅读(28)  评论(0编辑  收藏  举报