whx带我刷JOI training

唔有几题几星期前已经做过了就不鸟它们了
【bzoj4237】
不错的题……
n x,y 坐标均不相同的点,求以这些点为左下角和右上角的矩阵的个数。
之前看到这题的时候刚好做了某逆序对题和这题似乎挺像……以为是扫描线+线段树,然后不会做2333
唔然后再看的时候YY了个分治+二分+可持久化线段树(雾)的东西……然而好像还是不能做
再后来whx告诉我……cdq分治+单调栈+二分……这是 O(nlog2n) 的……后来whx在散步时脑补出 nlogn 的并查集真是跪烂orz!!但是我觉得不太对……
我的做法:首先按x轴排序,然后按y轴分治。这个时候上面一半的点和下面一半的点已经按x排好了序。枚举上面的点,维护上半部分的点一个类似上凸壳的单调上升的点集和下半部分的点一个类似下凸壳的单调下降的点集,每次在下面的点集查询夹在了上面点集最后两个点之间的点的个数,更新答案。
而whx说的大概是把那些不在点集里的(也就是单调栈里面被删掉了的)点用并查集搞起来,然后就不需要二分查找……?不明觉厉……
my code(用了奇怪的常数优化2333):

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define maxn 262143

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

struct node {
    int x , y;
    node(int x = 0 , int y = 0):x(x) , y(y) { }
}p[maxn] , tmp[maxn] , sta1[maxn] , sta2[maxn];

int n , x[maxn] , y[maxn];

long long ans;

bool cmp_x(const node a , const node b) { return a.x < b.x ; }

bool cmp_y(const node a , const node b) { return a.y < b.y ; }

void input() {
    n = rd();
    rep(i , 1 , n) p[i].x = x[i] = rd() , p[i].y = y[i] = rd();
    sort(x + 1 , x + n + 1);
    sort(y + 1 , y + n + 1);
    rep(i , 1 , n) {
        p[i].x = lower_bound(x + 1 , x + n + 1 , p[i].x) - x;
        p[i].y = lower_bound(y + 1 , y + n + 1 , p[i].y) - y;
    }
    sort(p + 1 , p + n + 1 , cmp_x);
}

void merge(int l , int r) {
    if (l == r) return ;
    int m = (l + r) >> 1 , _l = l , _r = m + 1;
    rep(i , l , r) tmp[p[i].y <= m ? _l ++ : _r ++] = p[i];
    memcpy(p + l , tmp + l , sizeof(node) * (r - l + 1)) ;
    node*t1 = sta1 , *t2 = sta2;
    for(int i = m + 1 , j = l ; i <= r ; i ++) {
        for( ; j <= m && p[j].x < p[i].x ; j ++) {
            while (t2 != sta2 && p[j].y > (*t2).y) t2 --;
            *(++ t2) = p[j];
        }
        while (t1 != sta1 && p[i].y < (*t1).y) t1 --;
        int pos = upper_bound(sta2 + 1 , t2 + 1 , *t1 , cmp_x) - sta2;
        ans += (t2 - sta2) - pos + 1 , *(++ t1) = p[i];
    }
    merge(l , m);
    merge(m + 1 ,  r);
}

void solve() {
    merge(1 , n);
    printf("%lld\n" , ans);
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.txt" , "r" , stdin);
    #endif
    input();
    solve();
    return 0;
}

【bzoj4238】
whx太神啦!脑补出了超级厉害的解法!(虽然都没有写出来>.<)
最后还是膜了po姐的code……
太弱了TAT

#include <bits/stdc++.h>
#define For(i,a,b) for(int i=a;i<=b;i++)
#define fore(i,u) for(int i=head[u];i;i=nxt[i])
#define maxn 131071
#define maxm 524287

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

int to[maxm] , nxt[maxm] , head[maxm];
int fa[maxn] , vis[maxn] , dep [maxn];
int good[maxn] , bad[maxn];
int n , m , good_cnt , bad_cnt , ett;

inline void ins(int u , int v) {
    to[++ ett] = v , nxt[ett] = head[u] , head[u] = ett;
    to[++ ett] = u , nxt[ett] = head[v] , head[v] = ett;
}

void dfs(int u , int f) {
    dep[u] = dep[fa[u]] + 1;
    vis[u] = 1;
    fore(i , u) {
        int v = to[i];
        if (!(i ^ f ^ 1)) continue;
        if (!vis[v]) {
            fa[v] = u , dfs(v , i);
            good[u] += good[v] , bad[u] += bad[v];
        } else {
            if (dep[v] > dep[u]) continue;
            if ((dep[u] - dep[v]) & 1)
                good[u] ++ , good[v] -- , good_cnt ++;
            else
                bad[u] ++ , bad[v] -- , bad_cnt ++;
        }
    }
}

void input() {
    n = rd() , m = rd() , ett = 1;
    For(i , 1 , m) ins(rd() , rd());
}

void solve() {
    int ans = 0 ;
    For(i , 1 , n)
        if (!vis[i]) dfs(i , -1);
    For(i , 1 , n)
        if (fa[i] && bad[i] == bad_cnt && !good[i])
            ans ++;
    if (bad_cnt == 1) ans ++;
    printf("%d\n" , ans);
}

int main() {
    input();
    solve();
    return 0;
}


【bzoj4240】
就是通过调换把它弄成个“合唱队形”的东西
仔细yy一下和逆序对挺像的,每个数自己动的次数就是比它本身大的数
然后往两边搞一搞取个max就ok

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define cr(x) memset(x , 0 , sizeof(x))
#define lowbit(x) (x&(-x))
#define maxn 524287

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

typedef int arr[maxn];

arr S , h , a , ans;

int n , m ;

int sum(int x) {
    int ret = 0;
    while (x <= m)
        ret += S[x] , x += lowbit(x);
    return ret;
}

void add(int x) {
    while (x)
        S[x] ++ , x -= lowbit(x);
}

void input() {
    n = rd();
    rep(i , 1 , n) a[i] = h[i] = rd();
    sort(h + 1 , h + n + 1);
    m = unique(h + 1 , h + n + 1) - h - 1;
    rep(i , 1 , n) a[i] = lower_bound(h + 1 , h + m + 1 , a[i]) - h;
}

void solve() {
    rep(i , 1 , n) add(a[i]) , ans[i] = sum(a[i] + 1);
    cr(S) ; long long tot = 0;
    per(i , n , 1) add(a[i]) , tot += min(ans[i] , sum(a[i] + 1));
    printf("%lld\n" , tot);
}

int main() {
    input();
    solve();
    return 0;
}

【bzoj4242】
%%%whx太厉害啦
一开始我以为是二分然后动态构图。。。每次跑q个询问
并不能做。。。然后想到这东西挺像货车运输的。。。
想用贪心搞生成森林。。。发现不一定联通。。。
于是whx告诉我,把所有的建筑物仍进去跑多源最短路然后每个点都连边就好啦!直接跑400w的生成树!nlogn不虚!(大雾)然后whx又告诉我,注意到边权只有400w,所以开个400w的vector!每次把边塞到对应边权的vector里面!这样就可以 O(n) 了!还有个trick,在并查集合并的时候按大小合并同时控制父亲节点这样就可以把生成树的高度控制在 O(logn) ,于是后面就可以欢脱地不用倍增了
whx太强啦!whx太强啦!whx太强啦!

#include <bits/stdc++.h>
using namespace std;
#define For(i,a,b) for(int i=a;i<=b;i++)
#define forg(i,g) for(int i=0;i<(int)g.size();i++)
#define pb push_back
#define mp make_pair
#define pii pair<int , int>
#define key first
#define id second
#define mat 2003][2003
#define maxn 200023

const int fx[] = {0 , 0 , 1 , -1};
const int fy[] = {1 , -1 , 0 , 0};

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

inline void upmax(int&a , int b) { if (a < b) a = b ; }

int n , m , p , q , fa[maxn] , pa[maxn] , rk[maxn] , dep[maxn] , dis[maxn];
char Map[mat];
pii  v[mat];
vector<pii> G[2000*2000+1];
queue<pii> Q;

void input() {
    n = rd() , m = rd() , p = rd() , q = rd();
    For(i , 1 , n) {
        For(j , 1 , m) 
            Map[i][j] = getchar();
        getchar();
    }
    For(i , 1 , n) Map[i][m + 1] = Map[i][0] = '#';
    For(i , 1 , m) Map[n + 1][i] = Map[0][i] = '#';
    For(i , 1 , p) {
        int x = rd() , y = rd();
        v[x][y] = mp(0 , i);
        Q.push(mp(x , y));
    }
}

void bfs() {
    while (!Q.empty()) {
        pii u = Q.front() ; Q.pop();
        int x = u.first , y = u.second;
        int d = v[x][y].key , p = v[x][y].id;
        For(i , 0 , 3) {
            int nx = x + fx[i] ,
                ny = y + fy[i] ;
            if (Map[nx][ny] == '#') continue;
            pii&k = v[nx][ny];
            if (k == mp(0 , 0)) {
                k = mp(d + 1 , p);
                Q.push(mp(nx , ny));
            } else if (k.id != p)
                G[d + k.key].pb(mp(k.id , p));
        }
    }
}

int find(int x) {
    return pa[x] == x ? x : pa[x] = find(pa[x]);
}

void mst() {
    For(i , 1 , p) pa[i] = i;
    For(i , 0 , m * n) forg(j , G[i]) {
        int x = G[i][j].key ,
            y = G[i][j].id  ;
        x = find(x) , y = find(y);
        if (x == y) continue;
        if (rk[x] > rk[y]) swap(x , y);
        if (rk[x] == rk[y]) rk[y] ++;
        pa[x] = y , fa[x] = y , dis[x] = i;
    }
}

inline int query(int u , int v) {
    if (find(u) != find(v)) return -1;
    int ret = 0;
    if (dep[u] < dep[v]) swap(u , v);
    while (dep[u] > dep[v])
        upmax(ret , dis[u]) , u = fa[u];
    while (u != v)
        upmax(ret , max(dis[u] , dis[v])) ,
        u = fa[u] , v = fa[v];
    return ret;
}

void find_dep(int u) {
    if (!fa[u])
        { dep[u] = 1 ; return ; }
    find_dep(fa[u]);
    dep[u] = dep[fa[u]] + 1;
}

void solve() {
    bfs();
    mst();
    For(i , 1 , p) if (!dep[i]) find_dep(i);
    For(i , 1 , q) {
        int x = rd() , y = rd();
        printf("%d\n" , query(x , y));
    }
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.txt" , "r" , stdin);
    #endif
    input();
    solve();
    return 0;
}

/ * 挖坑待填* /

你可能感兴趣的:(whx带我刷JOI training)