九、十月刷题记录

天天爱跑步

细节较多的一道。
将树变成以1为根。
首先是考虑路径如何对每个点做贡献。
当问题简化为一个简单区间的问题,我们选择用桶(或者双指针)记录满足条件的路径数目,对每个点进行贡献。这里用了类似想法,不过是区间变成了树,那么就需要用到树上差分来处理桶。
对于每个点,满足要求的路径有两种,一种是从其子树出发来到这个点(上行),一种是从其祖先出发来到这个点(下行)。观察发现,可以对观察者 j j j 做贡献的上行路径 i i i 满足: d e p [ j ] + w [ j ] = d e p [ s ] dep[j]+w[j]=dep[s] dep[j]+w[j]=dep[s] ,下行路径满足: d i s t [ s , t ] − w [ j ] = d e p [ t ] − d e p [ j ] dist[s,t]-w[j]=dep[t]-dep[j] dist[s,t]w[j]=dep[t]dep[j] d i s t [ s , t ] − d e p [ t ] = w [ j ] − d e p [ j ] dist[s,t]-dep[t]=w[j]-dep[j] dist[s,t]dep[t]=w[j]dep[j] 。(桶)
然后这些路径要满足穿过了点 j j j 。(这一点用树上差分来保证)
具体来说,对于每个点,穿过该点的路径对桶的贡献为递归到该点时和回溯时的桶增加量。对于每个点,回溯时增加以该点为起点或终点的路径,贡献该点,离开时删除以该点为lca的路径(因为回溯时,都是该点的祖先,以该点为lca的路径显然不穿过其祖先)。
删除重复贡献的路径的重复贡献(即起点或终点为lca的路径)。

//
// Created by Artist on 2021/9/25.
//

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define Dbuk1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }

const int maxn = 3e5+5;
const int LOG = __lg(maxn)+1;
vector<int> G[maxn];
vector<int> lc[maxn]; // lca
vector<int> ed[maxn];

int dist[maxn],dep[maxn],w[maxn],ans[maxn],f[maxn][LOG+3];
struct node{
    int x,y;
}a[maxn];

int dfs0(int u,int fa){
    f[u][0] = fa;
    dep[u]=dep[fa]+1;
    for(auto v:G[u]) if(v-fa) dfs0(v,u);
}

int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=LOG;~i;--i) if(dep[f[x][i]]>=dep[y]&&f[x][i]) x=f[x][i];
    if(x==y) return x;
    for(int i=LOG;~i;--i) if(f[x][i]!=f[y][i]&&f[x][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}


// 上行:dep[s]=w[j]+dep[j],下行:dis[s,t]-w[j]=dep[t]-dep[j]
// 桶:dep[s]=dep[j]+w[j],      dis[s,t]-dep[t]=w[j]-dep[j]
int buk1[maxn<<1],buk2[maxn<<1],js[maxn];

void solve(int u,int fa){
    int tmp1=buk1[w[u]+dep[u]];
    int tmp2=buk2[w[u]-dep[u]+maxn];
    for(auto v:G[u]) if(v-fa) solve(v,u);
    buk1[dep[u]]+=js[u]; // 将当前点作为起点的树链贡献上
    for(auto i:ed[u]) buk2[dist[i]-dep[a[i].y]+maxn]++; // 将当前点作为终点的树链贡献上
    ans[u]+=buk1[dep[u]+w[u]]-tmp1+buk2[w[u]-dep[u]+maxn]-tmp2;
    // 计算出递归完子树后的桶增加量
    // 若有链的lca位于当前点,那么他们对当前点的父亲不会有贡献,删去
    for(auto i:lc[u]) {
        buk1[dep[a[i].x]]--;
        buk2[dist[i]-dep[a[i].y]+maxn]--;
    }
}
signed main() {
    io();
    int n,m;cin>>n>>m;
    for(int i=1;i<n;++i) {
        int x,y;cin>>x>>y;
        G[x].pb(y);
        G[y].pb(x);
    }
    dfs0(1,0);
    for(int j=1;j<=LOG;++j) for(int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1];
    for(int i=1;i<=n;++i) cin>>w[i];
    for(int i=1;i<=m;++i) {
        int x,y;cin>>x>>y;a[i].x=x,a[i].y=y;
        int LCA = lca(x,y);
        js[x]++;
        ed[y].pb(i);
        lc[LCA].pb(i);
        dist[i] = dep[x]+dep[y]-2*dep[LCA];
        if(dep[LCA]+w[LCA]==dep[x]) ans[LCA]--;
    }
    solve(1,0);
    for(int i=1;i<=n;++i) {
        cout<<ans[i]<<" ";
    }
    cout<<endl;
}

cf1572 B. Xor of 3(构造)

操作后奇偶性不变+全部异或起来后的性质。(奇偶分情况讨论的思想)

//
// Created by Artist on 2021/9/29.
//

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
const int maxn = 2e5+5;
int a[maxn];
vector<int> ans;
signed main() {
    //io();
    int t;cin>>t;
    while(t--) {
        int n;cin>>n;
        for(int i=1;i<=n;++i) cin>>a[i];
        int xs=0;
        for(int i=1;i<=n;++i) xs^=a[i];
        if(xs&1) {
            cout<<"NO"<<endl;
            continue;
        }
        ans.clear();
        if(n&1) {
            cout<<"YES"<<endl;
            for(int i=1;i<n;i+=2) {
                ans.pb(i);
            }
            for(int i=n-4;i>0;i-=2) {
                ans.pb(i);
            }
            cout<<ans.size()<<endl;
            for(auto i:ans) cout<<i<<" ";
            cout<<endl;
            continue;
        }
        // 8
        // 1 0 1 1 1 0 1 1
        int flg=0;
        for(int i=1;i<n;i++) {
            xs^=a[i];
            if((i&1)&&(!xs)) {
                flg=i;
                break;
            }
        }
        if(!flg) cout<<"NO"<<endl;
        else {
            cout<<"YES"<<endl;
            for(int i=1;i<=flg-2;i+=2) {
                ans.pb(i);
            }
            for(int i=flg-4;i>0;i-=2) {
                ans.pb(i);
            }
            for(int i=flg+1;i<n;i+=2) {
                ans.pb(i);
            }
            for(int i=n-4;i>flg;i-=2) {
                ans.pb(i);
            }
            cout<<ans.size()<<endl;
            for(auto i:ans) cout<<i<<" ";
            cout<<endl;
        }
    }
}

E. National Property

2-SAT求一组解板子

//
// Created by artist on 2021/9/30.
//


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'
#define pii pair<int,int>

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
const int maxn = 1e5+5;
vector<int> let[maxn];
//             每一列
vector<int> G[maxn<<1];
vector<int> G2[maxn<<1];
int len[maxn];

int scc,idx,dfn[maxn<<1],low[maxn<<1],ind[maxn<<1],belong[maxn<<1],opp[maxn<<1];

bool in[maxn<<1],vis[maxn<<1],color[maxn<<1];

stack<int> S;

void tarjan(int u) {
    dfn[u] = low[u] = ++idx;
    S.push(u);
    in[u] = true;
    for(auto v:G[u]) {
        if(!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(in[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u]) {
        int v;
        do {
            v = S.top();
            S.pop();
            in[v] = false;
            belong[v] = scc;
        }
        while(u != v);
        scc++;
    }
}

void get_scc(int n) {
    scc = idx = 0;
    memset(dfn,0,sizeof(dfn));
    memset(in,false,sizeof(in));
    for(int i=0; i<n; i++)
        if(!dfn[i])
            tarjan(i);
}

bool conflict(int n) {
    for(int i=0; i<n; i++)
        if(belong[i<<1] == belong[i<<1|1])
            return true;
        else {
            opp[ belong[i<<1] ] = belong[i<<1|1];
            opp[ belong[i<<1|1] ] = belong[i<<1];
        }
    return false;
}

void rebuild(int n) {
    memset(ind,0,sizeof(ind));
    for(int u=0; u<n; u++) {
        for(int v:G[u]) {
            if(belong[u] == belong[v])
                continue;
            ind[belong[u]]++;//ni'tu
            G2[belong[v]].pb(belong[u]);
        }
    }
}

void paint(int u) {
    for(int v:G2[u]) {
        if(!vis[v]) {
            vis[v] = true;
            paint(v);
        }
    }
}

void topSort() {
    memset(vis,false,sizeof(vis));
    memset(color,false,sizeof(color));
    queue<int> q;
    for(int i=0; i<scc; i++)
        if(!ind[i])
            q.push(i);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        if(vis[u])
            continue;
        vis[u] = true;
        color[u] = true;
        int v = opp[u];
        if(!vis[v]) {
            vis[v] = true;
            paint(v);
        }
        for(int v2:G2[u]) {
            if(--ind[v2] == 0)
                q.push(v2);
        }
    }
}


vector<int> ans;
signed main() {
    io();
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;++i) {
        cin>>len[i];
        let[i].pb(-1);
        for(int j=1;j<=len[i];++j) {
            int x;cin>>x;x--;
            let[i].pb(x);
        }
    }
    int flg=0;
    for(int i=1;i<n;++i) {
        // 当前小于下一个
        for(int j=1;j<=min(len[i+1],len[i]);++j) {
            if(let[i][j]==let[i+1][j]) {
                if(j==len[i+1]&&len[i]>len[i+1]) {
                    flg=1;
                }
                continue;
            }
            if(let[i][j]<let[i+1][j]) {
                G[let[i][j]<<1].pb(let[i+1][j]<<1);
                G[let[i+1][j]<<1|1].pb(let[i][j]<<1|1);
            } else {
                // 当前一定是大写,下一个一定是小写
                G[let[i][j]<<1].pb(let[i][j]<<1|1);
                G[let[i+1][j]<<1|1].pb(let[i+1][j]<<1);
            }
            break;
        }
    }
    if(flg) return cout<<"No",0;
    get_scc(m<<1);
    if(conflict(m))
        cout<<"No"<<endl;
    else
    {
        rebuild(m<<1);
        topSort();
        cout<<"Yes"<<endl;
        for(int i=0; i<m; i++){
            if(!color[belong[i<<1]]) ans.pb(i+1);
        }
        cout<<ans.size()<<endl;
        for(int i:ans) cout<<i<<" ";
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值