版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sugar_free_mint/article/details/86652278
tarjan太强了
JZOJ 3894 洛谷 3365 改造二叉树
题目
把一棵二叉树变为二叉查找树最少要修改多少个节点
分析
二叉查找树有一条性质,中序遍历后权值严格单调递增,所以说可以跑一遍中序遍历再用二分求
,但是貌似还是有问题,为什么呢,such as
这个图中序遍历为
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 数字对
题目
一个特殊区间满足,存在一个 ,并且对于任意的 , 都能被 整除。这样的一个特殊区间 价值为 。
分析
然而这道题数据比较水,
就可以过,枚举
,左右两边扩张即可
正解应该是
,但是好像比
慢,而且
代码
#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