2019.01.25【NOIP提高组】模拟 B 组

111 篇文章 0 订阅
47 篇文章 0 订阅

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(LkR),并且对于任意的 i ( L ≤ i ≤ R ) i(L\leq i\leq R) i(LiR) a i a_i ai都能被 a k a_k ak整除。这样的一个特殊区间 [ L ∼ R ] [L\sim R] [LR]价值为 R − L R - L RL


分析

然而这道题数据比较水, 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值