数据结构
数据结构
单调栈
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;
}