5.18 题解

一篇题解需要一个头图。

A

状压 BFS。

考虑状压下标 i 和坐标 x,y 的转化,容易发现 x = i >> 2, y = i & 3,而 i == x << 2 | y

剩下的就好写了。

#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
int s, t, d[100050], f[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};queue<int> q;
int main()
{
    memset(d, -1, sizeof d);
    for(int i = 0, x;i < 16;++i) scanf("%1d", &x), s |= x << i;
    for(int i = 0, x;i < 16;++i) scanf("%1d", &x), t |= x << i;
    d[s] = 0;q.push(s);while(!q.empty())
    {
        int u = q.front();q.pop();
        if(u == t) {printf("%d", d[u]);break;}
        for(int i = 0, x, y;i < 16;++i) if(u & 1 << i)
            for(int j = 0, x = i >> 2, y = i & 3, a, b, k, v;j < 4;++j)
            {
                a = x + f[j][0];b = y + f[j][1];
                if(a >= 0 && a < 4 && b >= 0 && b < 4 && !(u & 1 << (k = a << 2 | b))
                    && !~d[v = u & ~(1 << i) | 1 << k]) d[v] = d[u] + 1, q.push(v);
            }
    }
    return 0;
}

B

啊这题我做过啊,不对我怎么 40pts。

Sol 1

直接用一个数据结构维护这个牌堆,发现需要区间平移和单点删除,FHQ 维护之。

#include <cstdio>
#include <cstdlib>
inline int R()
{
    int q = 0;char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') q = q * 10 + c - '0', c = getchar();
    return q;
}
void P(int x) {if(x >= 10) P(x / 10);putchar(x % 10 + '0');}
struct T
{
    T *l, *r;int v, k, s;
    T(int _) : l(0), r(0), v(_), k(rand()), s(1) {}
    void u() {s = 1;if(l) s += l->s;if(r) s += r->s;}
}*r, *a, *b, *c;
void S(T *x, int k, T *&a, T *&b)
{
    if(!x) {a = b = 0;return;}int z = x->l ? x->l->s : 0;
    if(z >= k) b = x, S(x->l, k, a, b->l), b->u();
    else a = x, S(x->r, k - z - 1, a->r, b), a->u();
}
T *M(T *a, T *b)
{
    if(!a) return b;if(!b) return a;
    if(a->k < b->k) {a->r = M(a->r, b);a->u();return a;}
    else {b->l = M(a, b->l);b->u();return b;}
}
int n, x;
int main()
{
    srand(388651);
    n = R();for(int i = 1;i <= n;++i) r = M(r, new T(i));while(n--)
        x = R() % r->s, S(r, x, a, c), S(c, 1, b, c), P(b->v), puts(""), r = M(c, a);
    return 0;
}

洛谷上 AC 了,然后因为神秘原因被卡常了。

Sol 2

维护 ci 表示 i 这张牌是否在牌堆中。考虑维护一个指针表示当前牌堆顶,然后去找这个指针下一次移动到哪。

若当前指针在 pp 刚被发出去),之后需要销 r 张牌,则 p 移动到 o,其中 o 满足 ri=po1ci(modci)

树状数组维护 ci,然后树状数组上倍增找 o

#include <cstdio>
#include <algorithm>
using namespace std;
int n, p, c[700050];
void M(int x) {for(;x <= n;x += x & -x) --c[x];}
int Q(int x, int y)
{
    int q = 0;for(--x;y > x;y &= y - 1) q += c[y];
    for(;x > y;x &= x - 1) q -= c[x];return q;
}
int K(int k)
{
    int q = 0, s = 0;for(int i = __lg(n), x, y;i >= 0;--i)
        if((x = q | 1 << i) <= n && (y = s + c[x]) < k) q = x, s = y;
    return q + 1;
}
int main()
{
    scanf("%d", &n);for(int i = 1;i <= n;++i) c[i] = i & -i;
    for(int i = 0, r, z;i < n;++i)
    {
        scanf("%d", &r);r = r % (n - i) + 1;
        if((z = Q(1, p) + r) <= n - i) M(p = K(z)), printf("%d\n", p);
        else M(p = K(r - Q(p, n))), printf("%d\n", p);
    }
    return 0;
}

啥卡常没加直接到最优解 rk3 了,树状数组上倍增这么小众的吗

C

发现如果没有不能往回走的限制就是一个裸倍增 Floyd,所以考虑怎么把这个限制去掉。

考虑点边互换,V=E,边 uv|u,vV 存在当且仅当原图上 u,v 首尾相接。

特别地,为了满足不能往回走的限制,原图上 uv,vu 的正反两条边之间在新图上不连边。

则新图上一条长度为 k+1 的路径唯一对应一条原图上长度为 k 的满足条件的路径。

在新图上建 S=ns,T=tn+1,倍增 Floyd 求出 ST 有多少条长度为 k+1 的路径即可。

#include <cstdio>
#include <cstring>
#define M 45989
int n, m, k, s, t, c, u[160], v[160];
struct S
{
    int a[160][160];
    S() {memset(a, 0, sizeof a);}
    S operator*(S b)
    {
        S q;
        for(int i = 1;i <= c;++i)
            for(int j = 1;j <= c;++j)
                for(int k = 1;k <= c;++k)
                    q.a[i][j] = (q.a[i][j] + a[i][k] * b.a[k][j]) % M;
        return q;
    }
}b, q;
int main()
{
    scanf("%d%d%d%d%d", &n, &m, &k, &s, &t);
    u[++c] = n;v[c] = s;
    for(int i = 0, x, y;i < m;++i)
        scanf("%d%d", &x, &y), u[++c] = x, v[c] = y, u[++c] = y, v[c] = x;
    u[++c] = t;v[c] = n + 1;++k;
    for(int i = 1;i <= c;++i)
        for(int j = 1;j <= c;++j)
            if(i != j && i != (j ^ 1) && v[i] == u[j])
                b.a[i][j] = 1;
    for(int i = 1;i <= c;++i) q.a[i][i] = 1;
    for(;k;k >>= 1, b = b * b)
        if(k & 1) q = q * b;
    printf("%d", q.a[1][c]);
    return 0;
}

D

树上 DP。设 fi,j=xsubtree(i)[d(i,x)=j],gi,j=x,ysubtree(i),xy[d(i,x)=d(i,y)=d(x,y)]

posted @   Jijidawang  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示