数据结构

数据结构


单调栈

https://xlor.cn/2019/4/2019nanchang/


笛卡尔树

Equivalent Prefixes-前缀笛卡尔树

序列u,v 对于\([1,ans]\)上所有的\([L,R]\)\((1<=L<=R<=ans<=n)\)

都满足\(RMQ(u,L,R)=RMQ(v,L,R)\)

\(max(ans)\);

分析:考虑判断两个序列的前缀笛卡尔树是否相等

注意,如果前缀笛卡尔树相等,则可以判断每一个栈深都相同,想想为什么?、

#include <bits/stdc++.h>
#include<stdint.h>
#define int long long
#define scan(n) scanf("%lld", &(n))
#define scann(n, m) scanf("%lld%lld", &(n), &(m))
#define scannn(a, b, c) scanf("%lld%lld%lld", &(a), &(b), &(c))
#define prin(n) printf("%lld", (n))
#define pb push_back
#define mp make_pair
#define ms(a) memset(a, 0, sizeof(a))
#define fo(i, a, b) for (int i = (a); i <= (b); i++)
#define ro(i, a, b) for (int i = (a); i >= (b); i--)
const int inf = 0x3f3f3f3f;
using namespace std;
const int maxn = 1e5+100;
int n;
int a[maxn],b[maxn];
int s1[maxn],s2[maxn];
int32_t main() {
    while(scan(n)!=EOF){
        fo(i,1,n)scan(a[i]);
        fo(i,1,n)scan(b[i]);
        int head1=1,head2=1,tail1=1,tail2=1;
        s1[head1]=s2[head2]=inf;
        int maxpos=1;
        fo(i,1,n){
            while(head1<=tail1&&s1[tail1]>a[i])tail1--;
            while(head2<=tail2&&s2[tail2]>b[i])tail2--;
            if(tail1!=tail2)break;
            s1[++tail1]=a[i],s2[++tail2]=b[i];
            maxpos=i;
        }
        cout<<maxpos<<endl;
    }
    return 0;
}

笛卡尔树习题

http://acm.hdu.edu.cn/showproblem.php?pid=6305

https://blog.csdn.net/zhaiqiming2010/article/details/80245872


Bitset

#include<bits/stdc++.h>
using namespace std;

bitset<5> b, a;
int main() {
    cout << b << endl; //00000

    b.set(2);
    cout << b << endl;//00100

    a.set(3);
    cout << a << endl;//01000

    b ^= a;//(00100)^(01000)
    cout << b << endl;//01100

    b.reset(3);//00100
    cout << b << endl;

    b.flip();//11011
    cout << b << endl;
}

LCA

[CF1304E]1-Trees and Queries

https://codeforces.com/contest/1304/problem/E

给一棵树,每次查询时在x和y之间加一条边,然后问在a和b之间是否存在一条可以重复走的路径且这条路径的长度为k

如果不走x和y的那条连边,那么a到b之间的路径长度为a与b的最短路径+2i,因为重复走的边对答案的贡献一定是偶数长度,只有走了x到y的那条连边,贡献为1,可以改变路径的奇偶性,枚举两种情况即可

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
int head[maxn], to[maxn * 2], nxt[maxn * 2], d[maxn * 2], tot;
int n, m;
void add(int x, int y, int w){
    to[++tot] = y; nxt[tot] = head[x]; d[tot] = w; head[x] = tot;
}
int dp[maxn][20], dep[maxn], dis[maxn];
void dfs(int u, int fa){
    dp[u][0] = fa; dep[u] = dep[fa] + 1;
    for (int i = head[u]; i; i = nxt[i]){
        int v = to[i];
        if (v == fa) continue;
        dis[v] = dis[u] + d[i];
        dfs(v, u);
    }
}
void init(){
    memset(dp, 0, sizeof(dp));
    dep[0] = dis[0] = 0;
    dfs(1, 0); 
    for (int j = 1; j < 20; j++) 
        for (int i = 1; i <= n; i++) 
            dp[i][j] = dp[dp[i][j - 1]][j - 1];
}
int qlca(int x, int y){
    if (dep[x] < dep[y]) swap(x, y);
    int tmp = dep[x] - dep[y];
    for (int i = 0; tmp; i++, tmp >>= 1)
        if (tmp & 1) x = dp[x][i];
    if (x == y) return x;
    for (int i = 19; i >= 0; i--){
        if (dp[x][i] != dp[y][i]){
            x = dp[x][i]; y = dp[y][i];
        }
    }
    return dp[x][0];
}
int dist(int x,int y) {
    int u = qlca(x, y);
    int ans =  dis[x] + dis[y] - 2*dis[u];
    return dis[x] + dis[y] - 2*dis[u];
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n-1;i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y, 1);
        add(y, x, 1);
    }
    init();
    int q;
    scanf("%d", &q);
    while(q--) {
        int a, b, x, y,k;
        scanf("%d%d%d%d%d", &x, &y, &a, &b, &k);
        int d = dist(a, b);
        if(k>=d&&(k-d)%2==0) {
            printf("YES\n");
            continue;
        }
        else {
            d = dist(a,x)+dist(b,y)+1;
            if(k>=d&&(k-d)%2==0) {
                printf("YES\n");
                continue;
            }
            d = dist(a,y)+dist(b,x)+1;
            if(k>=d&&(k-d)%2==0) {
                printf("YES\n");
                continue;
            }
        }
        printf("NO\n");
    }
}

树状数组

Codeforces1324DPair of Topics

https://codeforces.com/contest/1324/problem/D

给定\(a,b\)序列,求有多少对\(i<j\)\(a_i+a_j>b_i+b_j\)

解法1:按照值从大到小插入

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int a[maxn];
int b[maxn];
int c[maxn];
struct Node
{
    int v, index;
    bool operator<(const Node& b) const {
        if(v==b.v) {
            return index <b.index;//注意这里!!
        }
        return v < b.v;  //从小到大排序 
	}
}node[maxn];
int n;
void add(int i)
{
	while(i<=n*2)
	{
		c[i]++;
		i+=i&(-i);	
	}
}
int sum(int i)
{
	int res=0;
	while(i>0)
	{
		res+=c[i];
		i-=i&(-i);
	}
	return res;
}
vector<int> v;
int main() {
    scanf("%d", &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++) {
        node[i].v = a[i] - b[i];
        node[i].index = i;
        node[i + n].v = b[i] - a[i];
        node[i + n].index = i + n;
    }
    long long ans = 0;
    sort(node + 1, node + 1 + 2 * n);
    for (int i = 2*n; i >=1;i--) {
        int id = node[i].index;
        int v = node[i].v;
        if(id<=n) {
            add(node[i].index);
            continue;
        }
        if(v<0)
            ans--;
        ans += sum(id - n);
    }
 
    cout<<ans<<endl;
}

解法2:按照序列顺序从小到大

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
int a[maxn], b[maxn], c[maxn * 2];
int lowbit(int x) {
    return x & (-x);
}

void add(int i,int d) {
    while(i<maxn*2) {
        c[i] += d;
        i += lowbit(i);
    }
}

int query(int i) {
    int ret = 0;
    while(i>0) {
        ret += c[i];
        i -= lowbit(i);
    }
    return ret;
}

vector<int> v;

int main() {
    int n;
    scanf("%d", &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++) {
        v.push_back(a[i] - b[i]);
        v.push_back(b[i] - a[i]);
    }
    long long ans = 0;
    sort(v.begin(), v.end());
    for (int i = 1;i<=n;i++) {
        int x = b[i] - a[i];
        int y = a[i] - b[i];
        int id = upper_bound(v.begin(), v.end(), x) - v.begin() + 1;
        int id2 = upper_bound(v.begin(), v.end(), y) - v.begin() + 1;
        ans += (i - 1 - query(id));
        add(id2, 1);
    }
    cout << ans << endl;
}

打一顿!!不用树状数组

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
vector<int> v;
int a[maxn],b[maxn];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n;i++) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= n;i++) {
        scanf("%d", &b[i]);
        v.push_back(a[i] - b[i]);
    }
    sort(v.begin(), v.end());
    long long ans = 0;
    for (int i = 1; i <= n;i++) {
        int id = upper_bound(v.begin(), v.end(), b[i] - a[i]) - v.begin();
        ans += (n  - id);
        if(a[i]-b[i]>b[i]-a[i])
            ans--;
    }
    ans /= 2;
    cout << ans << endl;
}

[CF1325C] Ehab and Path-etic MEXS

https://codeforces.com/contest/1325/problem/C

给一棵树的边附上0~n-2的权值,使得Mex(u,v)的最大值最小

思路:只需要考虑0,1,2的情况

​ 对于所有不经过0的路径,mex = 0

​ 对于所有不经过1\2,但经过0的路径,mex = 1\2

​ 这样就可以满足所有情况

#include<bits/stdc++.h>
using namespace std;
 
const int maxn = 1e5 + 5;
struct node{
    int to, id;
};
vector<node> e[maxn];
int x[maxn], y[maxn];
int vis[maxn];
int ans[maxn];
int l, r;
void dfs(int u,int fa) {
    int sz = e[u].size();
    for (int i = 0; i < sz;i++) {
        int v = e[u][i].to;
        int id = e[u][i].id;
        if(v==fa||vis[id])
            continue;
        ans[id] = l++;
    }
    for (int i = 0;i<sz;i++) {
        int v = e[u][i].to;
        int id = e[u][i].id;
        if(v==fa||vis[id])
            continue;
        vis[id]++;
        dfs(v, u);
    }
}
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n - 1;i++) {
        scanf("%d %d", &x[i], &y[i]);
        e[x[i]].push_back((node){y[i], i});
        e[y[i]].push_back((node){x[i], i});
    }
    l = 0, r = n - 2;
    int d = 0;
    int m = 0;
    for (int i = 1; i <= n;i++) {
        int sz = e[i].size();
        if(sz>d) {
            d = sz;
            m = i;
        }
    }
 
    dfs(m, -1);
    for (int i = 0; i < n-1;i++) {
        printf("%d\n", ans[i]);
    }
    return 0;
}

[CF1325F]Ehab's Last Theorem

令t为ceil(sqrt(n))

解法一:考虑DFS树的原理,如果从\(u\)\(v\),经过的不是\((u,v)\)这条边,说明先前存在另一条路径使得\(u\)\(v\),那么此时可以形成一个环,具体的见这篇文章: https://codeforces.com/blog/entry/68138

并且!如果一个点的dfs子树中所有的点都不能与它满足构成长度大于t的环这个条件,也就是情况2,那么这个点就可以存在于独立点集中

 if(!vis[u]) {
        for(int v:e[u]) {
            vis[v] = 1;
        }
    }

u必然不会存在与一个长度大于t的环当中,那么它必然在独立集里,则把它的连边全部标记起来。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 100;
int t;
int dep[maxn];
int f[maxn],vis[maxn];
vector<int> e[maxn];
void dfs(int u,int fa) {
    for(int v:e[u]) {
        if(!dep[v]) {
            dep[v] = dep[u] + 1;
            f[v] = u;
            dfs(v, u);
        } 
        else {
            if(dep[v]-dep[u]+1>=t) {
                printf("2\n");
                int len = dep[v] - dep[u] + 1;
                printf("%d\n", len);
                int cnt = 0;
                while(v) {
                    if(cnt==len)
                        break;
                    cnt++;
                    printf("%d ", v);
                    v = f[v];
                }
                exit(0);
            }
        }
    }
    if(!vis[u]) {
        for(int v:e[u]) {
            vis[v] = 1;
        }
    }
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    t = ceil(sqrt(n));
    for (int i = 1; i <= m;i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dep[1] = 1;
    dfs(1, 0);
    printf("1\n");
    int cnt = 0;
    for (int i = 1; i <= n;i++) {
        if(cnt==t)
            break;
        if(!vis[i]) {
            printf("%d ", i);
            cnt++;
        }
    }
}

解法2:

一个结论:如果一张图里的每个点的度数都大于\(d\),那么必然存在一个环,长度为\(d+1\)

在寻找环的过程中将所有度数小于\(t-1\)的点的邻边都删掉,这个点作为独立集的备选

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
set<pair<int,int> > s;
vector<int> v[maxn];
bool del[maxn];
int deg[maxn],occ[maxn];
void remove(int x)
{
    if (del[x])
    return;
    s.erase({deg[x],x});
    del[x]=1;
    for (int u:v[x])
    {
        if (!del[u])
        {
            s.erase({deg[u],u});
            deg[u]--;
            s.insert({deg[u],u});
        }
    }
}

int main()
{
    int n, m,sq;
    scanf("%d%d",&n,&m);
    sq = ceil(sqrt(n));
    while (m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        v[a].push_back(b);
        v[b].push_back(a);
        deg[a]++;
        deg[b]++;
    }

    for (int i=1;i<=n;i++)
    s.insert({deg[i],i});
    vector<int> ans;
    while (!s.empty())
    {
        auto p=*s.begin();
        s.erase(s.begin());
        if (p.first+1>=sq)//度数大于等于sq-1
        {
            printf("2\n");
            vector<int> d({p.second});
            occ[p.second]=1;
            while (1)
            {
                pair<int,int> nex(1e9,0);
                for (int u:v[d.back()])
                {
                    if (!del[u])
                    nex=min(nex,make_pair(occ[u],u));
                }
                if (nex.first)
                {
                    printf("%d\n",(int)d.size()-nex.first+1);
                    for (int i=nex.first-1;i<d.size();i++)
                    printf("%d ",d[i]);
                    return 0;
                }
                d.push_back(nex.second);
                occ[nex.second]=d.size();
            }
        }
        //度数小于sq-1
        ans.push_back(p.second);
        remove(p.second);//删除所有邻边
        for (int u:v[p.second])//双向删除
        remove(u);
    }
    printf("1\n");
    for (int i=0;i<sq;i++)
    printf("%d ",ans[i]);
}

[CF1139C] Edgy Tree

https://codeforces.com/contest/1139/problem/C

题意:一棵树,且需要构建长度为k的序列,序列中相邻两点的路径一定经过"黑边",节点可以重复

思路:"黑边"不加入连通块,将"红边"的各个连通块的大小找出来,减去这一部分构造的长度为k的序列

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
vector<int> e[maxn];
int vis[maxn];
const long long mod = 1e9 + 7;
#define add(x, y) (x + y) % mod;
int dfs(int u,int fa) {
    int sz = e[u].size();
    long long ret = 1;
    vis[u]++;
    for (int i = 0; i < sz;i++) {
        int v = e[u][i];
        if(v==fa||vis[v]){
            continue;
        }
        ret += dfs(v, u);
    }
    return ret;
}
long long qpow(long long x,int k) {
    long long ret = 1;
    while(k) {
        if(k&1)
            ret = ret * x % mod;
        x = x * x % mod;
        k >>= 1;
    }
    return ret;
}
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n - 1;i++) {
        int x, y, w;
        scanf("%d%d%d", &x, &y, &w);
        if(!w) {
            e[x].push_back(y);
            e[y].push_back(x);
        }
    }
    long long sum = 0;
    for (int i = 1; i <= n; i++) {
        if (vis[i])
            continue;
        long long cnt = dfs(i, -1);
        long long x = qpow(cnt, k);
        sum = add(sum, x);
    }
    long long ans = qpow(n, k);
    ans = (ans - sum + mod) % mod;
    cout << ans << endl;
}
posted @ 2020-03-27 23:56  瓜瓜站起来  阅读(209)  评论(0编辑  收藏  举报