2015-2016 Petrozavodsk Winter Training Camp, Moscow SU Trinity Contest

2015-2016 Petrozavodsk Winter Training Camp, Moscow SU Trinity Contest
大佬题解
A:
一看就是求矩阵的秩,不知道为啥WA,好像被卡精度了,自闭。。

E:
这题是阅读题。。。首先找到LCA,把a和b每次向上提时,将其父亲的另一个儿子加入到集合中。把a和b都提到LCA后,把LCA往上提,做同样的操作。

#include 
using namespace std;
int main() {
     
    int a, b;
    scanf("%d%d", &a, &b);
    vector<int> ans;
    ans.push_back(a);
    ans.push_back(b);
    int t=a;
    set<int> s;
    while(t) {
     
        s.insert(t);
        t/=2;
    }
    int lca;
    t = b;
    while(t) {
     
        if(s.count(t)) {
     
            lca = t;
            break;
        }
        t/=2;
    }
    if(lca==a||lca==b) {
     
        puts("-1");
        return 0;
    }
    t = a;
    while(t/2!=lca) {
     
        ans.push_back(t/2*2+1-t%2);
        t/=2;
    }
    t = b;
    while(t/2!=lca) {
     
        ans.push_back(t/2*2+1-t%2);
        t/=2;
    }
    t=lca;
    while(t/2) {
     
        ans.push_back(t/2*2+1-t%2);
        t/=2;
    }
    sort(ans.begin(), ans.end());
    for(int i=0; i<ans.size(); ++i) printf("%d%c", ans[i], i+1==ans.size()?'\n':' ');
}

F:
树上莫队。
树上莫队的思想就是将树转化为字典序,然后在上面进行修改和删除。首先dfs一次求出字典序, f [ u ] f[u] f[u] 表示进入 u u u 点的字典序, g [ u ] g[u] g[u] 表示出 u u u 点的字典序。对于维护点权,如果 [ L , R ] [L,R] [L,R] 区间的字典序上已经有这个点,那么就删除这个点,如果没这个点,那么就添加这个点。在这种情况下,我们可以观察到:假定 f [ u ] < f [ v ] f[u]<f[v] f[u]<f[v] ,也就是说 u u u v v v 先dfs到,那么如果它们的LCA是 u u u v v v 的其中一个, 那么对应的询问区间应该是 [ f [ u ] , f [ v ] ] [f[u], f[v]] [f[u],f[v]] ,如果LCA另有它值,那么对应的询问区间应该是 [ g [ u ] , f [ v ] ] [g[u], f[v]] [g[u],f[v]] ,且这种情况下区间里肯定是没有它们的LCA的,因此计算答案时还要加上它们的LCA。
对于维护边权,我们将边权转化为其儿子的点权。那么问题反过来了,如果LCA是本身,才要加LCA。
但其实这题可以不用管LCA的,直接求区间 [ f [ u ] + 1 , f [ v ] ] [f[u]+1, f[v]] [f[u]+1,f[v]] 就满足条件了。
维护mex主要思路是将权值分块,因此添加/删除只需要修改计数器即可,复杂度为 O ( 1 ) O(1) O(1) 。查询在块上查找,复杂度是 O ( n ) O(\sqrt n) O(n ) 。因此总的复杂度还是 O ( n 3 2 ) O(n^{\frac{3}{2}}) O(n23)

#include 
using namespace std;
const int N = 2e5+7;
const int WB = 500;
const int WL = 207;
int f[N], g[N], id[N], n, q, wf[N], fa[N][22], dep[N], index;
int pos[N], ans[N], bc[WL], cnt[N];
bool vis[N];
struct Edge {
     
    int v, w;
};
vector<Edge> adj[N];
struct Query{
     
    int l, r, id;
    bool operator < (const Query& rhs) const {
     
        return pos[l]<pos[rhs.l] || (pos[l]==pos[rhs.l]&&r<rhs.r);
    }
}qs[N];
int lca(int x, int y) {
     
    if(dep[x]<dep[y]) swap(x, y);
    if(dep[x]!=dep[y]) {
     
        int dis = dep[x]-dep[y];
        for(int i=20; i>=0; --i) {
     
            if(dis>=(1<<i)) dis-=(1<<i), x=fa[x][i];
        }
    }
    if(x==y) return x;
    for(int i=20; i>=0; --i) {
     
        if(fa[x][i]!=fa[y][i]) x=fa[x][i], y=fa[y][i];
    }
    return fa[x][0];
}
void dfs(int u, int w) {
     
    id[f[u]=++index]=u;
    wf[u]=w;
    for(Edge e : adj[u]) {
     
        if(e.v!=fa[u][0]) {
     
            fa[e.v][0]=u;
            dep[e.v] = dep[u]+1;
            dfs(e.v, e.w);
        }
    }
    id[g[u]=++index]=u;
}
void add(int x) {
     
//    printf("add %d\n", x);
    int w = wf[x];
    int b = w/WB;
    if(cnt[w]==0) {
     
        ++bc[b];
    }
    ++cnt[w];
}
void del(int x) {
     
//    printf("del %d\n", x);
    int w = wf[x];
    int b = w/WB;
    if(cnt[w]==1) {
     
        --bc[b];
    }
    --cnt[w];
}
void modify(int x) {
     
    if(wf[x]>=100000) return;
    if(vis[x]) {
     
        del(x);
        vis[x] = false;
    } else {
     
        add(x);
        vis[x] = true;
    }
}
int query() {
     
    for(int i=0; i<WL; ++i) {
     
        if(bc[i]!=WB) {
     
            for(int j=i*WB; j<(i+1)*WB; ++j) {
     
                if(!cnt[j]) return j;
            }
        }
    }
    assert(false);
}
int main() {
     
    scanf("%d%d", &n, &q);
    for(int i=1; i<n; ++i) {
     
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        adj[u].push_back({
     v, w});
        adj[v].push_back({
     u, w});
    }
    dfs(1, N);
    for(int j=1; j<=20; ++j) {
     
        for(int i=1; i<=n; ++i) {
     
            fa[i][j]=fa[fa[i][j-1]][j-1];
        }
    }
    int B = sqrt(index);
    for(int i=1; i<=index; ++i) {
     
        pos[i] = (i-1)/B;
    }
    for(int i=0; i<q; ++i) {
     
        int l, r;
        scanf("%d%d", &l, &r);
        if(f[l]>f[r]) swap(l, r);
        qs[i] = {
     lca(l, r)==l?f[l]:g[l], f[r], i};
//        printf("qq: %d %d\n", qs[i].l, qs[i].r);
    }
//    printf("dfs xv:\n");
//    for(int i=1; i<=index; ++i) printf("%d%c", id[i], i==index?'\n':' ');
    sort(qs, qs+q);
    int L, R;
    L=1; R=0;
    for(int i=0; i<q; ++i) {
     
        while(L>qs[i].l) modify(id[--L]);
        while(R<qs[i].r) modify(id[++R]);
        while(L<qs[i].l) modify(id[L++]);
        while(R>qs[i].r) modify(id[R--]);
        int x = id[L], y = id[R];
        int llca = lca(x, y);
        if(llca==x||llca==y) {
     
            modify(llca);
            ans[qs[i].id] = query();
            modify(llca);
        } else {
     
            ans[qs[i].id] = query();
        }
//        printf("ans[%d]=%d, lca=%d, L=%d, R=%d\n", qs[i].id, ans[qs[i].id], llca, L, R);
    }
    for(int i=0; i<q; ++i) {
     
        printf("%d\n", ans[i]);
    }
}

I :
首先,很容想到根据询问的右端点排序处理。然后维护一个非严格单调递减栈,就可以找出每个点作为山谷的右端点时,对应左端点的位置。我们维护一个单点修改区间最值的线段树,每次求得一个山谷区间时 [ L , R ] [L,R] [L,R] 时,将线段树的 L L L 位置更新为 R R R 。那么对于一个询问 [ L q , R q ] [L_q, R_q] [Lq,Rq] ,时,所有山谷区间的右端点小于 R q R_q Rq 的都应该更新到线段树里了。那么询问线段树 [ L q , R q ] [L_q, R_q] [Lq,Rq] 的最值即为答案。但这里出现了一个问题,因为每次更新线段树时,但是在山谷区间左端点 L L L 被弹出栈时进行更新的。但栈中可能有连续两个位置元素,它们对应值是相等的,虽然它们能形成山谷,但是并没有弹出栈,导致线段树并没有被更新。因此要对于这种情况特殊处理,观察栈 s s s 可以发现一个性质,就是 a [ s i ] ≥ a [ s i + 1 ] a[s_i] \geq a[s_{i+1}] a[si]a[si+1] ,对于没弹出栈的元素 s i s_i si ,它当前的山谷右端点是它距离它最右边且 a [ s j ] = a [ s i ] a[s_j]=a[s_i] a[sj]=a[si] 的第一个值。因此可以再开一个区间加,单调修改,区间最值得线段树维护这个答案。询问也一样。对于跨越了 L q L_q Lq 的连续相同值山谷,也需要特殊处理一下。(写完后想了想好像这里不需要特殊处理?待议。。。)
解决这个问题主要是要想到单调栈与每个点贡献的结合。。。

#include 
#define lson (rt<<1)
#define rson (rt<<1|1)
using namespace std;
const int INF = 1e9+7;
const int N = 5e5+7;
int a[N];
int rb[N];
int ans[N], l[N], r[N], p[N];
int mx[2][N<<2], lz[2][N<<2];
void push_down(int tid, int rt) {
     
    if(lz[tid][rt]) {
     
        lz[tid][lson] +=lz[tid][rt];
        lz[tid][rson] +=lz[tid][rt];
        mx[tid][lson] +=lz[tid][rt];
        mx[tid][rson] +=lz[tid][rt];
        lz[tid][rt] = 0;
    }
}
void push_up(int tid, int rt) {
     
    mx[tid][rt] = max(mx[tid][lson], mx[tid][rson]);
}
void update_add(int rt, int l, int r, int tid, int ql, int qr, int v) {
     
    if(ql<=l&&qr>=r) {
     
        lz[tid][rt]+=v;
        mx[tid][rt]+=v;
        return;
    }
    push_down(tid, rt);
    int m = (l+r)/2;
    if(ql<=m) update_add(lson, l, m, tid, ql, qr, v);
    if(qr>m) update_add(rson, m+1, r, tid, ql, qr, v);
    push_up(tid, rt);
}
void update_set(int rt, int l, int r, int tid, int p, int v) {
     
    if(l==r) {
     
        lz[tid][rt] = 0;
        mx[tid][rt] = v;
        return ;
    }
    push_down(tid, rt);
    int m =(l+r)/2;
    if(p<=m) update_set(lson, l, m, tid, p, v);
    else update_set(rson, m+1, r, tid, p, v);
    push_up(tid, rt);
}
int query(int rt, int l, int r, int tid, int ql, int qr) {
     
//    printf("query tree: %d %d %d\n", l, r, mx[tid][rt]);
    if(ql<=l&&qr>=r) return mx[tid][rt];
    push_down(tid, rt);
    int m = (l+r)/2;
    int res = 0;
    if(ql<=m) res = max(res, query(lson, l, m, tid, ql, qr));
    if(qr>m) res = max(res, query(rson, m+1, r, tid, ql, qr));
    push_up(tid, rt);
    return res;
}
int stk[N];
struct Stack {
     
    int tp;
    Stack():tp(0){
     }
    int size() {
      return tp; }
    void push(int val) {
      stk[tp++]=val; }
    int top() {
      return stk[tp-1]; }
    void pop() {
      --tp; }
    bool empty() {
      return tp==0; }
};
bool debug;
int main() {
     
//    freopen("I.out", "w", stdout);
    debug = false;
    int n, m;
    scanf("%d%d", &n, &m);
    Stack s;
    memset(rb, -1, sizeof(rb));
    for(int i=1; i<=n; ++i) scanf("%d", &a[i]);
    for(int i=0; i<m; ++i) {
     
        scanf("%d%d", &l[i], &r[i]);
        p[i]=i;
    }
    sort(p, p+m, [](int x, int y) {
     
            return r[x]<r[y];
        });
    int cur=0;
    for(int i=1; i<=n; ++i) {
     
        while(!s.empty()&&a[s.top()]<a[i]) {
     
            int R = s.top();
            while(!s.empty()&&a[s.top()]==a[R]) {
     
                if(debug) printf("pop index=%d, value=%d, set segtree0 on point %d to %d\n", s.top(), a[s.top()], s.top(), R-s.top());
                update_set(1, 1, n, 0, s.top(), R-s.top());
                if(debug) printf("pop index=%d, value=%d, reset segtree1 on point %d to 0\n", s.top(), a[s.top()], s.size());
                update_set(1, 1, n, 1, s.size(), 0);
                s.pop();
            }
        }
        int d = -1;
        if(!s.empty()) d=s.top();
        s.push(i);
        int id = lower_bound(stk, stk+s.size(), s.top(), [](int x, int y) {
     
                                return a[x]>a[y];
                             }) - stk; 
        if(id+1<s.size()) {
     
//            printf("stack content: ");
//            for(int j=0; j
//            puts("");
            if(d!=-1) d=i-d;
            else d=i;
            if(debug) printf("push index=%d, value=%d, add segtree1 on range [%d, %d] by %d\n", i, a[i], id+1, s.size()-1, d);
            update_add(1, 1, n, 1, id+1, s.size()-1, d); //加上新值
        }
        if(debug) printf("push index=%d, value=%d, set segtree1 on point %d to initial value 0\n", i, a[i], s.size());
        update_set(1, 1, n, 1, s.size(), 0); //新入栈元素的答案置0
        while(cur<m&&r[p[cur]]<=i) {
      //对于左端点小于等于i的询问
            int L = l[p[cur]], R=r[p[cur]];
            if(debug) printf("process query[%d, %d] on segtree0\n", L, R);
            int res = query(1, 1, n, 0, L, R); // 所有已出栈元素的贡献
            int id = lower_bound(stk, stk+s.size(), L)-stk; //询问左端点右边的第一个在栈中的值。
            int id2 = upper_bound(stk+id+1, stk+s.size(), stk[id], [](int x, int y) {
     
                                    return a[x]>a[y];
                                  })-stk; 
            if(id2!=s.size()) {
      //如果存在id2
                if(debug) printf("process query[%d, %d] on segtree1, query range=[%d, %d]\n", L, R, id2, s.size());
                int tmp = query(1, 1, n, 1, id2, s.size()); //询问答案
                res = max(res, tmp);
            }
            int tmp = stk[id2-1]-stk[id]; //跨越询问左端点的贡献。
            res = max(res, tmp);
            ans[p[cur]] = res;
            ++cur;
        }
    }
    int k = 0;
    for(int i=0; i<m; ++i) {
     
        printf("%d\n", ans[i]);
    }
}

J:
很容易看出是个树dp,维护当前点到叶子最多匹配的后缀和前缀。
但是一直WA,自闭了。

你可能感兴趣的:(比赛总结,莫队算法,线段树)