tarjan太强了
JZOJ 3894 洛谷 3365 改造二叉树
题目
把一棵二叉树变为二叉查找树最少要修改多少个节点
分析
二叉查找树有一条性质,中序遍历后权值严格单调递增,所以说可以跑一遍中序遍历再用二分求
l
i
s
lis
lis,但是貌似还是有问题,为什么呢,such as
这个图中序遍历为
1
,
2
,
4
,
3
,
5
,
6
,
6
,
8
,
7
,
9
1,2,4,3,5,6,6,8,7,9
1,2,4,3,5,6,6,8,7,9
lis得到的答案是不正确的,那为什么呢,因为这些数相互影响,那么把它们分别减去它们的下标,就可以保证严格单调递增
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
int n,l[100001],a1[100001],tot,len,r[100001],a[100001],b[100001];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void dfs(int x){
if (l[x]) dfs(l[x]);
a1[++tot]=a[x];
if (r[x]) dfs(r[x]);
}
signed main(){
n=iut();
for (rr int i=1;i<=n;++i) a[i]=iut();
for (rr int i=2;i<=n;++i){
rr int x=iut(),y=iut();
if (!y) l[x]=i; else r[x]=i;
}
dfs(1);
for (rr int i=1;i<=n;++i) a1[i]-=i;
for (rr int i=1;i<=n;++i){
if (a1[i]>=b[len]) b[++len]=a1[i];
else b[upper_bound(b+1,b+1+len,a1[i])-b]=a1[i];
}
return !printf("%d\n",n-len);
}
JZOJ 3895 数字对
题目
一个特殊区间满足,存在一个 k ( L ≤ k ≤ R ) k(L\leq k\leq R) k(L≤k≤R),并且对于任意的 i ( L ≤ i ≤ R ) i(L\leq i\leq R) i(L≤i≤R), a i a_i ai都能被 a k a_k ak整除。这样的一个特殊区间 [ L ∼ R ] [L\sim R] [L∼R]价值为 R − L R - L R−L。
分析
然而这道题数据比较水,
O
(
n
2
)
O(n^2)
O(n2)就可以过,枚举
a
k
a_k
ak,左右两边扩张即可
正解应该是
O
(
n
l
o
g
n
)
1637
m
s
R
M
Q
O(nlogn)1637msRMQ
O(nlogn)1637msRMQ,但是好像比
O
(
n
2
)
34
m
s
O(n^2)34ms
O(n2)34ms慢,而且
O
(
n
l
o
g
n
2
)
z
k
w
线
段
树
1875
m
s
O(nlogn^2)zkw线段树1875ms
O(nlogn2)zkw线段树1875ms
代码
#include <cstdio>
#include <cctype>
#include <queue>
#define rr register
using namespace std;
int n,a[500001],ans;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
signed main(){
n=iut(); rr queue<int>q;
for (rr int i=1;i<=n;++i) a[i]=iut();
for (rr int i=1,head,tail;i<=n;i=tail){
for (head=i-1;head;--head) if (a[head]%a[i]) break;
for (tail=i+1;tail<=n;++tail) if (a[tail]%a[i]) break;
if (tail-head-2>ans){
while (q.size()) q.pop();
q.push(head+1); ans=tail-head-2;
}
else if (tail-head==ans+2) q.push(head+1);
}
print(q.size()); putchar(32); print(ans);
putchar(10); print(q.front()); q.pop();
while (q.size()) putchar(32),print(q.front()),q.pop();
return 0;
}
JZOJ 3896 战争游戏 洛谷 3469 bzoj 1123 BLO-Blockade
题目
一个无向图,询问有多少个无序点对必经过任意点(洛谷题目要开long long,还要乘2)
分析
tarjan横空出世,那么tarjan是用来寻找割点的,那么除了割点以外其它点是确定的,割点除了普通点的答案,还要加上子树与其它子树或子树以外的答案
代码
#include <cstdio>
#include <cctype>
#define rr register
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
const int N=100010;
struct node{int y,next;}e[N<<1]; bool cut[N];
int k=1,ls[N],dfn[N],low[N],tot,son[N],ans[N],n=iut(),root;
inline void add(int x,int y){
if (x==y) return;
e[++k]=(node){y,ls[x]}; ls[x]=k;
e[++k]=(node){x,ls[y]}; ls[y]=k;
}
inline void tarjan(int x){
dfn[x]=low[x]=++tot; son[x]=1;
rr int flag=0,t=0;
for (rr int i=ls[x];i;i=e[i].next)
if (!dfn[e[i].y]){
tarjan(e[i].y); son[x]+=son[e[i].y];
low[x]=min(low[x],low[e[i].y]);
if (dfn[x]<=low[e[i].y]){
++flag; ans[x]+=son[e[i].y]*(n-son[e[i].y]);
t+=son[e[i].y]; if (x!=root||flag>1) cut[x]=1;
}
}else low[x]=min(low[x],dfn[e[i].y]);
if (cut[x]) ans[x]+=(n-t-1)*(t+1)+n-1;
else ans[x]=(n-1)<<1;
}
signed main(){
for (rr int m=iut();m;--m) add(iut(),iut());
for (rr int i=1;i<=n;++i) if (!dfn[i]) root=i,tarjan(i);
for (rr int i=1;i<=n;++i) print(ans[i]>>1),putchar(10);
return 0;
}
后续
表示不会Tarjan