天天爱跑步
细节较多的一道。
将树变成以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<<" ";
}
}