马上就下半年的区域赛了,应该是最后几场了。这里系统的而复习一下数据结构吧。

(先占坑,打cf去了)

(啊难死了***E题怎么比D题还简单啊啊啊没写完)

1.虚树

首先,虚树用于解决一类特殊的树形dp问题(目前遇见的是这样) 类似于,每次询问给你一个树的子集,在这个子集做dp 虚树实现的功能就是每次询问的时候,单独拿出“有用的点”建树 然后就是些一般的树形dp了 具体的,有用的点包括询问点和他们的lca 然后详细的讲解过程就不写了,挂一个学弟的博客吧

https://www.cnblogs.com/zwfymqz/p/9175152.html

建树的复杂度是O(k)的,具体是O(2*k),然后还要加上每次每次dp的复杂度

简单练习题 [Gym-100889J]

每次询问k个点中每个点到其他点(k个中)的最远距离 如果是简单的k个点的dp的话,dp[i][0]/[1]分别表示i往上往下的max距离

dp[i][0]=max{dp[v][0]+dis(i,v)}

dp[i][1]=max{dp[fa][1],dp[bro][0]}+dis[fa][i]}

这里多次询问且有修改,如果每次可以建立好虚树就和上面一样了 对于修改,就是一个简单的按dfs序建线段树然后区间修改单点查询

#include<bits/stdc++.h>

#define ll long long
#define lc k*2
#define rc k*2+1
#define mid (l+r)/2
#define maxn 100010
using namespace std;
ll n, m, lef[maxn], rig[maxn], dis[maxn], dep[maxn], dp[maxn][2];
ll s[maxn * 4], laz[maxn * 4], topt, fa[maxn][30], pos[maxn];
ll top, stck[maxn];
struct edge {
    ll v, t;
};
vector<edge> G[maxn];
vector<ll> P[maxn];

void Build(ll k, ll l, ll r) {
    if (l == r) {
        s[k] = dis[pos[l]];
        return;
    }
    Build(lc, l, mid);
    Build(rc, mid + 1, r);
    s[k] = s[lc] + s[rc];
}

void Up(ll k, ll l, ll r) {
    s[lc] += (mid - l + 1) * laz[k];
    s[rc] += (r - mid) * laz[k];
    laz[lc] += laz[k];
    laz[rc] += laz[k];
    laz[k] = 0;
}

void Insert(ll k, ll l, ll r, ll x, ll y, ll z) {
    if (x <= l && y >= r) {
        s[k] += (r - l + 1) * z;
        laz[k] += z;
        return;
    }
    if (laz[k])Up(k, l, r);
    if (x <= mid)Insert(lc, l, mid, x, y, z);
    if (y > mid)Insert(rc, mid + 1, r, x, y, z);
    s[k] = s[lc] + s[rc];
}

ll Query(ll k, ll l, ll r, ll x) {
    if (l == x && r == x)return s[k];
    if (laz[k])Up(k, l, r);
    if (x <= mid)return Query(lc, l, mid, x);
    else return Query(rc, mid + 1, r, x);
}

void Dfs(ll now, ll from, ll deep, ll dist) {
    lef[now] = ++topt;
    pos[topt] = now;
    dis[now] = dist;
    dep[now] = deep;
    fa[now][0] = from;
    for (edge e:G[now]) {
        if (e.v == from)continue;
        Dfs(e.v, now, deep + 1, dist + e.t);
    }
    rig[now] = topt;
}

void Get_fa() {
    for (ll j = 1; j <= 25; j++)
        for (ll i = 1; i <= n; i++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
}

ll LCA(ll x, ll y) {
    if (dep[x] < dep[y])swap(x, y);
    ll t = dep[x] - dep[y];
    for (ll i = 0; i < 25; i++)
        if (t & (1 << i))x = fa[x][i];
    if (x == y)return x;
    for (ll i = 25; i >= 0; i--)
        if (fa[x][i] != fa[y][i]) {
            x = fa[x][i];
            y = fa[y][i];
        }
    return fa[x][0];
}


void Add(ll p) {
    if (top == 0) {
        stck[++top] = p;
        return;
    }
    ll lca = LCA(p, stck[top]);
    if (lca == stck[top]) {
        stck[++top] = p;
        return;
    }
    while (top > 1 && lef[stck[top - 1]] >= lef[lca]) {
        P[stck[top - 1]].push_back(stck[top]);
        top--;
    }
    if (lca != stck[top]) {
        P[lca].push_back(stck[top]);
        stck[top] = lca;
    }
    stck[++top] = p;
}

struct node {
    ll oo, o;
} a[maxn];

bool cmp1(node x, node y) {
    return lef[x.oo] < lef[y.oo];
}

bool cmp2(node x, node y) {
    return x.o < y.o;
}

void DP1(ll now) {
    dp[now][0] = 0;
    for (ll v:P[now]) {
        DP1(v);
        ll di = Query(1, 1, n, lef[v]) - Query(1, 1, n, lef[now]);
        dp[now][0] = max(dp[now][0], dp[v][0] + di);
    }
}

void DP2(ll now, ll fa) {
    dp[now][1] = 0;
    if (fa != 0) {
        ll di = Query(1, 1, n, lef[now]) - Query(1, 1, n, lef[fa]);
        dp[now][1] = dp[fa][1] + di;
        for (ll v:P[fa]) {
            ll dii = Query(1, 1, n, lef[v]) - Query(1, 1, n, lef[fa]);
            if (v != now)dp[now][1] = max(dp[now][1], dp[v][0] + di + dii);
        }
    }
    for (ll v:P[now])DP2(v, now);
    P[now].clear();
}

int main() {
    scanf("%lld", &n);
    ll u, v, t, op, k;
    for (ll i = 1; i < n; i++) {
        scanf("%lld%lld%lld", &u, &v, &t);
        G[u].push_back(edge{v, t});
        G[v].push_back((edge{u, t}));
    }
    Dfs(1, 0, 1, 0);
    Get_fa();
    Build(1, 1, n);
    scanf("%lld", &m);
    while (m--) {
        scanf("%lld", &op);
        if (op == 1) {
            scanf("%lld%lld%lld", &u, &v, &t);
            for (edge &e:G[u])
                if (e.v == v) {
                    if (dep[u] > dep[v])Insert(1, 1, n, lef[u], rig[u], t - e.t);
                    else Insert(1, 1, n, lef[v], rig[v], t - e.t);
                    e.t = t;
                    break;
                }
        } else {
            scanf("%lld", &k);
            top = 0;
            for (ll i = 1; i <= k; i++) {
                scanf("%lld", &a[i].oo);
                a[i].o = i;
            }
            sort(a + 1, a + 1 + k, cmp1);
            for (ll i = 1; i <= k; i++)Add(a[i].oo);
            while (top > 1) {
                P[stck[top - 1]].push_back(stck[top]);
                top--;
            }
            sort(a + 1, a + 1 + k, cmp2);
            DP1(stck[1]);
            DP2(stck[1], 0);
            for (ll i = 1; i <= k; i++)
                printf("%lld ", max(dp[a[i].oo][0], dp[a[i].oo][1]));
            printf("\n");
        }
    }
    return 0;
}

2.笛卡尔树

哇马上熄灯了,先占一个代码明天再说

笛卡尔树结构由Vuillmin在解决范围搜索的几何数据结构问题时提出的,从数列中构造一棵笛卡尔树可以线性时间完成,需要采用基于栈的算法来找到在该数列中的所有最近小数。由此可知,笛卡尔树是一种特定的二叉树数据结构,可由数列构造,在范围最值查询、范围top k查询(range top k queries)等问题上有广泛应用。它具有堆的有序性,中序遍历可以输出原数列。

笛卡尔树是一棵二叉树,树的每个节点有两个值,一个为key,一个为value。光看key的话,笛卡尔树是一棵二叉搜索树,每个节点的左子树的key都比它小,右子树都比它大;光看value的话,笛卡尔树有点类似堆,根节点的value是最小(或者最大)的,每个节点的value都比它的子树要大。

/*
2019牛客多校1 A
由题意 两颗笛卡尔树同构
 */
#include<bits/stdc++.h>

#define maxn 100010
using namespace std;
int n, a[maxn], b[maxn], s1[maxn], s2[maxn], top1, top2;
int fa1[maxn], fa2[maxn], lc1[maxn], lc2[maxn], rc1[maxn], rc2[maxn];

int main() {
    while (~scanf("%d", &n)) {
        top1 = top2 = 0;
        int ans = n;
        for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++)scanf("%d", &b[i]);
        for (int i = 1; i <= n; i++) {
            while (top1 && a[s1[top1]] > a[i])
                lc1[i] = s1[top1], top1--;
            fa1[i] = s1[top1];
            fa1[lc1[i]] = i;
            if (fa1[i])rc1[fa1[i]] = i;
            s1[++top1] = i;

            while (top2 && b[s2[top2]] > b[i])
                lc2[i] = s2[top2], top2--;
            fa2[i] = s2[top2];
            fa2[lc2[i]] = i;
            if (fa2[i])rc2[fa2[i]] = i;
            s2[++top2] = i;
            if (top1 != top2) {
                ans = i - 1;
                break;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

HDU 6305RMQ Similar Sequence

首先不难想到B序列构成的笛卡尔树和A长得一样,由于B序列是实数,我们就假设bi互不相同,然后对于任意一个序列,每个数期望为0.5,期望的和为0.5n,下面考虑这个序列合法的概率:1/Πsiz[i] siz为每个子树的大小,也就是 根节点rot是整个序列最大的 概率为1/n,rot.lc是1~rot.key-1里面最大的 概率为1/(rot.key-1),其他同理


#include<bits/stdc++.h>

#define ll long long
#define mod 1000000007
#define maxn 1000010
using namespace std;
ll T, n, a[maxn], top, s[maxn], siz[maxn];
struct node {
    ll fa, lc, rc;
} t[maxn];

ll Build() {
    top = 0;
    for (ll i = 1; i <= n; i++) {
        ll k = top;
        while (k && a[s[k]] < a[i])k--;  //栈中比当前元素""的都出栈
        if (k) {  //find it,栈中元素没有完全出栈,当前元素为栈顶元素的右孩子
            t[i].fa = s[k];
            t[s[k]].rc = i;
        }
        if (k < top) {    //出栈的元素为当前元素的左孩子
            t[s[k + 1]].fa = i;
            t[i].lc = s[k + 1];
        }
        s[++k] = i;//当前元素入栈
        top = k;//top指向栈顶元素
    }
    t[s[1]].fa = 0;//遍历完成后的栈顶元素就是根
    return s[1];
}

void Dfs(ll now) {
    if (now == 0)return;
    siz[now] = 1;
    Dfs(t[now].lc);
    Dfs(t[now].rc);
    siz[now] += siz[t[now].lc] + siz[t[now].rc];
}

ll qc(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1)res = res * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}

ll inv(ll x) {
    return qc(x, mod - 2);
}

int main() {
    scanf("%lld", &T);
    while (T--) {
        scanf("%lld", &n);
        top = 0;
        for (ll i = 1; i <= n; i++) {
            scanf("%lld", &a[i]);
            t[i].fa = t[i].lc = t[i].rc = 0;
        }
        ll root = Build();
        Dfs(root);
        ll ans = n * inv(2) % mod;
        for (ll i = 1; i <= n; i++)
            ans = ans * inv(siz[i]) % mod;
        printf("%lld\n", ans);
    }
    return 0;
}
/*
 1
 5
 3 1 5 4 2
 */

3.主席树

其实准备靠后一点再写主席树的,但是今天正好刷笛卡尔树的题用主席树给做了就一块写了

简单放一下代码吧

/*基础离散化+区间第k大*/
#include<cstdio>
#include<algorithm>
#define maxn 60010
#define mid (l+r)/2
using namespace std;
int n,m,num,a[maxn],A[maxn],s[maxn],lc[maxn],rc[maxn],r[maxn],cnt;
int init(){
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
int Build(int S,int L,int R){
    cnt++;s[cnt]=S;
    lc[cnt]=L;rc[cnt]=R;
    return cnt;
}
void Insert(int &now,int pre,int l,int r,int k){
    now=Build(s[pre]+1,lc[pre],rc[pre]);
    if(l==r)return;
    if(k<=mid)Insert(lc[now],lc[pre],l,mid,k);
    else Insert(rc[now],rc[pre],mid+1,r,k);
}
int Query(int L,int R,int l,int r,int k){
    if(l==r)return l;
    int sum=s[lc[R]]-s[lc[L]];
    if(sum>=k)return Query(lc[L],lc[R],l,mid,k);
    else return Query(rc[L],rc[R],mid+1,r,k-sum);
}
int main()
{
    n=init();m=init();
    for(int i=1;i<=n;i++){
        a[i]=init();A[i]=a[i];
    }
    sort(A+1,A+1+n);
    num=unique(A+1,A+1+n)-A-1;
    for(int i=1;i<=n;i++){
        int pos=lower_bound(A+1,A+1+num,a[i])-A;
        Insert(r[i],r[i-1],1,num,pos);
    }
    for(int i=1;i<=m;i++){
        int L=init(),R=init();
        int pos=(R-L)/2+1;
        pos=Query(r[L-1],r[R],1,num,pos);
        printf("%d\n",A[pos]);
    }
    return 0;
}

然后是区间<=x的数有几个

这个题主席树的做法不难想 分治之后就是裸的主席树了,但是好像要两个log,而且我还是不知道这题为啥要用笛卡尔树(另外windows下手测他好像爆栈了,还特地去ubuntu跑了跑)

#include<bits/stdc++.h>

#define ll long long
#define mid (l+r)/2
#define maxn 100010
using namespace std;
int n, m, a[maxn], A[maxn], s[maxn * 20], lc[maxn * 20], rc[maxn * 20], rot[maxn], cnt;
int mx[maxn][25], p[maxn];
ll ans;

void ST() {
    for (int i = 1; i <= n; i++)
        mx[i][0] = i;
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            if (a[mx[i][j - 1]] > a[mx[i + (1 << j - 1)][j - 1]])mx[i][j] = mx[i][j - 1];
            else mx[i][j] = mx[i + (1 << j - 1)][j - 1];

    for (int i = 0; i <= 20; i++)
        if ((1 << i) <= n)p[1 << i] = i;
    for (int i = 1; i <= n; i++)
        if (!p[i])p[i] = p[i - 1];
}

int Query(int l, int r) {
    int k = p[r - l + 1];
    if (a[mx[l][k]] > a[mx[r - (1 << k) + 1][k]])return mx[l][k];
    else return mx[r - (1 << k) + 1][k];
}

int Build(int S, int L, int R) {
    cnt++;
    s[cnt] = S;
    lc[cnt] = L;
    rc[cnt] = R;
    return cnt;
}

void Insert(int &now, int pre, int l, int r, int k) {
    now = Build(s[pre] + 1, lc[pre], rc[pre]);
    if (l == r)return;
    if (k <= mid)Insert(lc[now], lc[pre], l, mid, k);
    else Insert(rc[now], rc[pre], mid + 1, r, k);
}

int Query(int L, int R, int x, int y, int l, int r) {
    if(x>y)return 0;
    if (x <= l && y >= r)return s[R] - s[L];
    int res = 0;
    if (x <= mid)res += Query(lc[L], lc[R], x, y, l, mid);
    if (y > mid)res += Query(rc[L], rc[R], x, y, mid + 1, r);
    return res;
}

int Ask(int l, int r, int x) {
    if (l > r)return 0;
//    printf("%d %d %d\n",l,r,x);
    int pos = upper_bound(A + 1, A + 1 + m, x) - A - 1;
    return Query(rot[l - 1], rot[r], 1, pos, 1, m);
}

void Dfs(int l, int r) {
    int pos = Query(l, r);
    if (pos - l < r - pos) {
        for (int i = l; i <= pos; i++)
            ans += Ask(pos, r, a[pos] / a[i]);
    } else {
        for (int i = pos; i <= r; i++)
            ans += Ask(l, pos, a[pos] / a[i]);
    }
    if (l <= pos - 1)Dfs(l, pos - 1);
    if (pos + 1 <= r)Dfs(pos + 1, r);
}

int main() {
//    n=100000;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
//        a[i]=rand()+1;
//        a[i]=i;
        A[i] = a[i];
    }
    ST();
    sort(A + 1, A + 1 + n);
    m = unique(A + 1, A + 1 + n) - A - 1;
    for (int i = 1; i <= n; i++) {
        int pos = lower_bound(A + 1, A + 1 + m, a[i]) - A;
        Insert(rot[i], rot[i - 1], 1, m, pos);
    }
    Dfs(1, n);
    printf("%lld\n", ans);
    return 0;
}

4.斯坦纳树(雾)

今天突然想起来这个东西,之前做过一个题,然而并没有记住,南昌邀请赛A题就GG了。今天整理一下(不过他好像不算数据结构)

可以用DP求解,dp[i][state]表示以i为根,指定集合中的点的连通状态为state的生成树的最小总权值。

转移方程有两重:

第一重,先通过连通状态的子集进行转移。dp[i][state]=min{ dp[i][subset1]+dp[i][subset2] } 枚举子集的技巧可以用 for(sub=(state-1)&state;sub;sub=(sub-1)&state)。

第二重,在当前枚举的连通状态下,对该连通状态进行松弛操作。dp[i][state]=min{ dp[i][state], dp[j][state]+e[i][j] }

为什么只需对该连通状态进行松弛?因为更后面的连通状态会由先前的连通状态通过第一重转移得到,所以无需对别的连通状态松弛。松弛操作用SPFA即可。

复杂度 O(n3^k+cE2^k)

/*
[WC2008]游览计划
就比较模板的题,但是输出路径的时候卡住了
之前的输出路径都是一条链走下来,这个不一定,可能回分叉
所以加一点特判
*/
#include<bits/stdc++.h>

#define maxn 60010
using namespace std;
int n, m, num, st[11][11], f[11][11][maxn], vis[11][11][maxn], g[11][11];
char as[11][11];
int pre[11][11][maxn][3];
struct node {
    int x, y;
};
queue<node> q;

int xx[4] = {0, 0, 1, -1};
int yy[4] = {1, -1, 0, 0};

void SPFA(int s) {
    while (!q.empty()) {
        node now = q.front();
        q.pop();
        vis[now.x][now.y][s] = 0;
        for (int k = 0; k < 4; k++) {
            int nx = now.x + xx[k];
            int ny = now.y + yy[k];
            if (nx >= 1 && nx <= n && ny >= 1 && ny <= m) {
                int ss = s | st[nx][ny];
                if (f[nx][ny][ss] > f[now.x][now.y][s] + g[nx][ny]) {
                    f[nx][ny][ss] = f[now.x][now.y][s] + g[nx][ny];
                    pre[nx][ny][ss][0] = now.x;
                    pre[nx][ny][ss][1] = now.y;
                    pre[nx][ny][ss][2] = s;
                    if (ss == s && vis[nx][ny][ss] == 0) {
                        vis[nx][ny][ss] = 1;
                        q.push(node{nx, ny});
                    }
                }
            }
        }
    }
}

void Dfs(int x, int y, int s) {
    as[x][y] = 'o';
    int nx = pre[x][y][s][0];
    int ny = pre[x][y][s][1];
    int ss = pre[x][y][s][2];
    if (!ss) return;
    Dfs(nx, ny, ss);
    if (nx == x && ny == y)Dfs(x, y, (s - ss) | st[x][y]);

}

int main() {
    scanf("%d%d", &n, &m);
    int has = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            scanf("%d", &g[i][j]);
            if (g[i][j] == 0) {
                num++;
                has = 1;
                st[i][j] = 1 << num - 1;
            }
        }
    memset(f, 127 / 3, sizeof(f));
    int lim = 1 << num;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (st[i][j])f[i][j][st[i][j]] = 0;
    for (int s = 1; s < lim; s++) {
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++) {
                if (st[i][j] && !(s & st[i][j]))continue;
                for (int ss = (s - 1) & s; ss; ss = (ss - 1) & s) {
                    int x = ss | st[i][j];
                    int y = (s - ss) | st[i][j];
                    if (f[i][j][s] > f[i][j][x] + f[i][j][y] - g[i][j]) {
                        f[i][j][s] = f[i][j][x] + f[i][j][y] - g[i][j];
                        pre[i][j][s][0] = i;
                        pre[i][j][s][1] = j;
                        pre[i][j][s][2] = x;
                    }
                }
                if (f[i][j][s] < f[0][0][0] && vis[i][j][s] == 0) {
                    q.push(node{i, j});
                    vis[i][j][s] = 1;
                }
            }
        SPFA(s);
    }
    int ans = 1e9;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            ans = min(ans, f[i][j][lim - 1]);
    if (has == 0)ans = 0;
    printf("%d\n", ans);
    int ok = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++)
            if (st[i][j]) {
                Dfs(i, j, lim - 1);
                ok = 1;
                break;
            }
        if (ok)break;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++)
            if (g[i][j] == 0)printf("x");
            else if (as[i][j] == 'o')printf("o");
            else printf("_");
        printf("\n");
    }

    return 0;
}

HDU4085 Peach Blossom Spring

/*
根据题目,并不是给定集合互相可达,而是匹配
其实就是斯坦纳森林,先把互相可达的状态跑出来
然后重新dp一下
 */
 #include<bits/stdc++.h>

#define maxn 60010
using namespace std;
int T, n, m, k, f[51][maxn], dp[maxn], vis[51][maxn], st[51];
struct edge {
    int v, t;
};
vector<edge> G[51];
queue<int> q;

void SPFA(int s) {
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u][s] = 0;
        for (edge e:G[u]) {
            int ss = s | st[e.v];
            if (f[e.v][ss] > f[u][s] + e.t) {
                f[e.v][ss] = f[u][s] + e.t;
                if (ss == s & vis[e.v][ss] == 0) {
                    vis[e.v][ss] = 1;
                    q.push(e.v);
                }
            }
        }
    }
}

bool check(int x) {
    int r = 0;
    for (int i = 0; x; i++, x >>= 1)
        r += (x & 1) * (i < k ? 1 : -1);
    return r == 0;
}

bool ok(int x) {
    if (x <= k)return 1;
    if (n - x < k)return 1;
    return 0;
}

int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d", &n, &m, &k);
        for (int i = 1; i <= n; i++) {
            G[i].clear();
            st[i] = 0;
        }
        int u, v, t;
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &u, &v, &t);
            G[u].push_back(edge{v, t});
            G[v].push_back(edge{u, t});
        }
        int lim = 1 << (2 * k);
        memset(f, 127 / 3, sizeof(f));
        memset(vis, 0, sizeof(vis));
        int p = 0;
        for (int i = 1; i <= n; i++) {
            f[i][0] = 0;
            if (ok(i)) {
                p++;
                st[i] = 1 << p - 1;
                f[i][st[i]] = 0;
            }
        }
        for (int s = 0; s < lim; s++) {
            for (int i = 1; i <= n; i++) {
                if (st[i] && !(s & st[i]))continue;
                for (int j = s & (s - 1); j; j = (j - 1) & s) {
                    f[i][s] = min(f[i][s], f[i][j | st[i]] + f[i][(s - j) | st[i]]);
                }
                if (f[i][s] < f[0][0]) {
                    q.push(i);
                    vis[i][s] = 1;
                }
            }
            SPFA(s);
        }
        for (int j = 0; j < lim; j++) {
            dp[j] = f[0][0];
            for (int i = 1; i <= n; i++)
                dp[j] = min(dp[j], f[i][j]);
        }
        for (int i = 1; i < lim; i++)
            if (check(i))
                for (int j = i & (i - 1); j; j = (j - 1) & i)
                    if (check(j))dp[i] = min(dp[i], dp[j] + dp[i - j]);
        if (dp[lim - 1] >= f[0][0])printf("No solution\n");
        else printf("%d\n", dp[lim - 1]);
    }
    return 0;
}

南昌邀请赛A题

#include<bits/stdc++.h>

#define maxn 60010
using namespace std;
int n, m, st[maxn], f[50][maxn], vis[50], dp[maxn];
map<string, int> mp;
queue<int> q;
struct edge {
    int v, t;
};
vector<edge> G[maxn];

void SPFA(int s) {
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (edge e:G[u]) {
            if (f[e.v][s] > f[u][s] + e.t) {
                f[e.v][s] = f[u][s] + e.t;
                if (vis[e.v] == 0) {
                    vis[e.v] = 1;
                    q.push(e.v);
                }
            }
        }
    }
}

bool ok(int s) {
    for (int i = 1; i <= 8; i += 2)
        if (((s >> i - 1) & 1) ^ ((s >> i) & 1))return 0;
    return 1;
}

int main() {
    scanf("%d%d", &n, &m);
    string name;
    for (int i = 1; i <= n; i++) {
        cin >> name;
        mp[name] = i;
    }
    string su, sv;
    int u, v, t;
    for (int i = 1; i <= m; i++) {
        cin >> su >> sv >> t;
        u = mp[su], v = mp[sv];
        G[u].push_back(edge{v, t});
        G[v].push_back(edge{u, t});
    }
    memset(f, 127 / 3, sizeof(f));
    for (int i = 1; i <= 8; i++) {
        cin >> su;
        u = mp[su];
        f[u][1 << i - 1] = 0;
    }
    int lim = 1 << 8;
    for (int s = 1; s < lim; s++) {
        for (int i = 1; i <= n; i++) {
            for (int j = s & (s - 1); j; j = (j - 1) & s) {
                f[i][s] = min(f[i][s], f[i][j] + f[i][s - j]);
            }
            if (f[i][s] < f[0][0]) {
                vis[i] = 1;
                q.push(i);
            }
        }
        SPFA(s);
    }
    for (int i = 0; i < lim; i++) {
        dp[i] = 1e9;
        if (ok(i)) {
            for (int j = 1; j <= n; j++)
                dp[i] = min(dp[i], f[j][i]);
        }
    }
    for (int i = 0; i < lim; i++)
        if (ok(i))
            for (int j = i & (i - 1); j; j = (j - 1) & i)
                dp[i] = min(dp[i], dp[j] + dp[i - j]);

    printf("%d\n", dp[lim - 1]);
    return 0;
}
/*
4 4
 1
 2
 3
 4
 1 2 5
 1 3 2
 1 4 6
 2 3 100
 1 2
 3 4
 1 3
 2 4
 */

5 李超树

之前一直以为这个树是个什么新的数据结构,今天看了看才发现好像就是一个标记永久化的线段树

解决的经典问题就是平面n条线段,支持在线加边,然后询问x位置的ymax

如果只n条直线的话应该也可以平衡树做

线段树按x轴建,插入新的线段的时候到某个区间,若区间空,则覆盖;若新的严格好于旧的,则覆盖;若有交点,就稍微讨论一下,把不确定的半个区间下放到孩子。最后取根到叶子路径上的所有直线max

/*
[JSOI2008]Blue Mary开公司
放直线 单点查max
*/
#include<bits/stdc++.h>

#define maxn 500010
#define db long double
#define lc k*2
#define rc k*2+1
#define mid (l+r)/2
using namespace std;
int n, m;
struct node {
    db k, b;
    int has;
} s[maxn * 4];

int dcmp(db x) {
    if (fabs(x) <= 1e-6)return 0;
    if (x > 0)return 1;
    else return -1;
}

db get(db k1, db b1, db k2, db b2) {
    if (dcmp(k1 - k2) == 0)return -1;
    return (b2 - b1) / (k1 - k2);
}

void Build(int k, int l, int r) {
    s[k].has = 0;
    if (l == r)return;
    Build(lc, l, mid);
    Build(rc, mid + 1, r);
}

void Insert(int k, int l, int r, db K, db B) {
    if (s[k].has == 0) {
        s[k].has = 1;
        s[k].k = K;
        s[k].b = B;
        return;
    }
    if (l == r) {
        if (s[k].k * l + s[k].b < K * l + B) {
            s[k].k = K;
            s[k].b = B;
        }
        return;
    }
    db c = get(s[k].k, s[k].b, K, B);
    if(c==-1&&B>s[k].b){
        s[k].k = K;
        s[k].b = B;
        return;
    }
    if (c <= mid) {
        if (dcmp(s[k].k - K) >= 0) {
            Insert(lc, l, mid, K, B);
        } else {
            Insert(lc, l, mid, s[k].k, s[k].b);
            s[k].k = K;
            s[k].b = B;
        }
    } else {
        if (dcmp(s[k].k - K) >= 0) {
            Insert(rc, mid + 1, r, s[k].k, s[k].b);
            s[k].k = K;
            s[k].b = B;
        } else {
            Insert(rc, mid + 1, r, K, B);
        }
    }
}

db Query(int k, int l, int r, int x) {
    if (s[k].has == 0)return 0;
    db res = s[k].k * x + s[k].b;
    if (l == x && r == x) return res;
    if (x <= mid)res = max(res, Query(lc, l, mid, x));
    else res = max(res, Query(rc, mid + 1, r, x));
    return res;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> m;
    n = 50000;
    string s;
    db k, b;
    int x;
    Build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        cin >> s;
        if (s == "Project") {
            cin >> b >> k;
            b -= k;
            Insert(1, 1, n, k, b);
        } else {
            cin >> x;
            if (x < 1 || x > n) {
                cout << 0 << endl;
                continue;
            }
            cout << int(Query(1, 1, n, x) / 100) << endl;
        }
    }
    return 0;
}
/*
6
Project 5 0
Project 1 1

Query 4
 Project 100 0
Query 10
Query 110
 */

另一道稍微复杂些的题目

/*
[SDOI2016]游戏
树上两点之间信息修改&&两点之间查询min
利用树剖对应到线段上
/*
#include<bits/stdc++.h>

#define endl "\n"
#define ll long long
#define maxn 100010
#define lc k*2
#define rc k*2+1
#define mid (l+r)/2
using namespace std;
ll n, m, num, cnt, head[maxn], sum[maxn], son[maxn], dep[maxn];
ll c[maxn], fa[maxn], top[maxn], d[maxn], cc[maxn];
struct A {
    ll k, b, v;
} s[maxn * 4];
const ll bas = 123456789123456789ll;
struct node {
    ll v, pre, t;
} e[maxn * 2];

void Add(ll from, ll to, ll dis) {
    num++;
    e[num].v = to;
    e[num].t = dis;
    e[num].pre = head[from];
    head[from] = num;
}


ll Dfs1(ll now, ll from, ll deep, ll dis) {
    fa[now] = from;
    dep[now] = deep;
    sum[now] = 1;
    d[now] = dis;
    for (ll i = head[now]; i; i = e[i].pre) {
        ll v = e[i].v;
        if (v == from)continue;
        sum[now] += Dfs1(v, now, deep + 1, dis + e[i].t);
        if (sum[son[now]] < sum[v])
            son[now] = v;
    }
    return sum[now];
}

void Dfs2(ll now, ll str, ll from) {
    c[now] = ++cnt;
    cc[cnt] = now;
    top[now] = str;
    if (!son[now])return;
    Dfs2(son[now], str, now);
    for (ll i = head[now]; i; i = e[i].pre) {
        ll v = e[i].v;
        if (v == from || v == son[now])continue;
        Dfs2(v, v, now);
    }
}

void Build(ll k, ll l, ll r) {
    s[k].k = 0;
    s[k].b = bas;
    s[k].v = bas;
    if (l == r)return;
    Build(lc, l, mid);
    Build(rc, mid + 1, r);
}

void in(ll k, ll l, ll r, ll K, ll B) {
    ll lx = s[k].k * d[cc[l]] + s[k].b;
    ll lxx = K * d[cc[l]] + B;
    ll rx = s[k].k * d[cc[r]] + s[k].b;
    ll rxx = K * d[cc[r]] + B;
    if (lxx >= lx && rxx >= rx)return;
    if (lxx <= lx && rxx <= rx) {
        s[k].k = K;
        s[k].b = B;
        s[k].v = min(s[k].v, min(lxx, rxx));
        return;
    }
    ll mx = s[k].k * d[cc[mid]] + s[k].b;
    ll mxx = K * d[cc[mid]] + B;
    if (lx < lxx) {
        if (mx > mxx) {
            in(lc, l, mid, s[k].k, s[k].b);
            s[k].k = K;
            s[k].b = B;
            s[k].v = min(lxx, rxx);
        } else {
            in(rc, mid + 1, r, K, B);
        }
    } else {
        if (mx > mxx) {
            in(rc, mid + 1, r, s[k].k, s[k].b);
            s[k].k = K;
            s[k].b = B;
            s[k].v =  min(lxx, rxx);
        } else {
            in(lc, l, mid, K, B);
        }
    }
    s[k].v = min(s[k].v, min(s[lc].v, s[rc].v));
}

void Insert(ll k, ll l, ll r, ll x, ll y, ll K, ll B) {
    if (x <= l && y >= r) {
        in(k, l, r, K, B);
        return;
    }
    if (x <= mid)Insert(lc, l, mid, x, y, K, B);
    if (y > mid)Insert(rc, mid + 1, r, x, y, K, B);
    s[k].v = min(s[k].v, min(s[lc].v, s[rc].v));
}

ll Query(ll k, ll l, ll r, ll x, ll y) {
    if (x <= l && y >= r)return s[k].v;
    ll res = bas;
    res = min(res, s[k].k * d[cc[max(l, x)]] + s[k].b);
    res = min(res, s[k].k * d[cc[min(r, y)]] + s[k].b);
    if (x <= mid)res = min(res, Query(lc, l, mid, x, y));
    if (y > mid)res = min(res, Query(rc, mid + 1, r, x, y));
    return res;
}

void Insertsum(ll x, ll y, ll K, ll B) {
    ll fx = top[x], fy = top[y];
    while (fx != fy) {
        if (dep[fx] >= dep[fy]) {
            Insert(1, 1, n, c[fx], c[x], K, B);
            x = fa[fx];
        } else {
            Insert(1, 1, n, c[fy], c[y], K, B);
            y = fa[fy];
        }
        fx = top[x];
        fy = top[y];
    }
    if (c[x] <= c[y])Insert(1, 1, n, c[x], c[y], K, B);
    else Insert(1, 1, n, c[y], c[x], K, B);
}

ll Querysum(ll x, ll y) {
    ll res = bas, fx = top[x], fy = top[y];
    while (fx != fy) {
        if (dep[fx] >= dep[fy]) {
            res = min(res, Query(1, 1, n, c[fx], c[x]));
            x = fa[fx];
        } else {
            res = min(res, Query(1, 1, n, c[fy], c[y]));
            y = fa[fy];
        }
        fx = top[x];
        fy = top[y];
    }
    if (c[x] <= c[y])res = min(res, Query(1, 1, n, c[x], c[y]));
    else res = min(res, Query(1, 1, n, c[y], c[x]));
    return res;
}

ll LCA(ll x, ll y) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]])swap(x, y);
        x = fa[top[x]];
    }
    if (dep[x] < dep[y])swap(x, y);
    return y;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m;
    ll x, y, z;
    for (ll i = 1; i < n; i++) {
        cin >> x >> y >> z;
        Add(x, y, z);
        Add(y, x, z);
    }
    Dfs1(1, -1, 0, 0);
    Dfs2(1, 1, -1);
    Build(1, 1, n);
    ll s, t, a, b, op, K, B, lca;
    while (m--) {
        cin >> op;
        if (op == 1) {
            cin >> s >> t >> a >> b;
            lca = LCA(s, t);
            K = -a;
            B = b + a * d[s];
            Insertsum(s, lca, K, B);
            K = a;
            B = b + a * d[s] - 2 * a * d[lca];
            Insertsum(lca, t, K, B);
        } else {
            cin >> s >> t;
            cout << Querysum(s, t) << endl;
        }
    }
    return 0;
}