练习曲
这是一个做题记录。
洛谷P1725 琪露诺
2023.8.5
标签:动态规划、单调队列。
一道动态规划题,先考虑暴力一点的做法:
设
转移方程:
时间复杂度为
暴力DP在本题的预计得分为60分,然而在开了氧气优化的情况下能直接过掉本题。
考虑一个时间复杂度更优的做法:
每次转移需要求一个区间的最大值,而每个区间都是从上一个区间右移一个单位得到的,因此可以使用单调队列优化。
题目存在部分细节需要注意:
暴力代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,dp[10211021],vis[10231311],L,R,ans=-0x7ffffff,w=0;
signed main(){
cin>>n>>L>>R;
memset(dp,-0x3f,sizeof(dp));
for (int i=0;i<=n;++i) {
cin>>vis[i];
}
dp[0]=0;
for (int i=L;i<=n+R-1;++i) {
for (int j=max(w,i-R);j<=i-L;++j) {
dp[i]=max(dp[i],dp[j]+vis[i]);
}
//cout<<dp[i]<<endl;
if (i>=n) ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}
单调队列优化代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+2023;
int n,m,l,r,a[N],dp[N],tail,q[N],p[N],ans=-1e9;
signed main(){
cin>>n>>l>>r;
for (int i=0;i<=n;++i) {
cin>>a[i];
}
memset(dp,~0x3f,sizeof(dp));
int head=tail=0,len=r-l+1;
dp[0]=0;
for (int i=0;i<=n-l;++i) {
while(head<=tail&&q[tail]<dp[i]) --tail;
q[++tail]=dp[i]; p[tail]=i;
while(head<=tail && p[head]<i-len+1) ++head;
dp[i+l]=q[head]+a[i+l];
if (i+r>=n) ans=max(ans,dp[i+l]);
//ans=max(ans,dp[i+l]);
}
cout<<ans;
return 0;
}
CF383C Propagating tree
2023.8.5
标签:树链剖分
题目需要我们对一个节点加上一个值,然后修改这个点的子树内各个点的值,从这个点到根节点每层
需要支持操作:子树修改,查询子树权值和。
子树修改和查询子树权值和不难想到树剖,但怎么在线段树上修改区间就成了问题。
不难发现一个节点的子树内每一层改变权值的符号相同(同一层一定都是加上一个数或者同一层都时减去一个数)。因此我们可以通过每个节点的深度的奇偶性判断这个数是加上这个值还是减去这个值。
即在
然而这样懒标记不好合并,如果选择舍弃懒标记直接单点修改的话,时间复杂度就会退化成
一个比较神奇的思路是:
修改时只考虑子树根节点深度的奇偶性,子树根节点深度为奇数就对整颗子树
查询时根据每个节点的奇偶性,加上或减去对应的值。(可以放在 push_down 里实现)。
这个题目不需要查询区间,因此树状数组更优秀一些,不过我还是更喜欢写线段树。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
char ch=getchar(); int x=0,f=0;
for (;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for (;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
void print(int x) {
if (x<0) { putchar('-'); x=-x; }
if (x>9) print(x/10);
putchar(x%10+48);
}
const int N=2e5+2023;
int n,m,a[N],head[N<<1],cnt;
int tot,dfn[N],pre[N],top[N],fa[N],son[N],dep[N],size[N];
struct node{
int next,to,w;
}e[N<<1];
void add(int u,int v) {
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
namespace ss{
#define lson pos<<1
#define rson pos<<1|1
struct node{
int sum,len,lazy,dep;
}tree[N<<2];
void build(int pos,int l,int r) {
tree[pos].len=r-l+1;
if (l==r) {
tree[pos].sum=a[pre[l]];
tree[pos].dep=dep[pre[l]];
return ;
}
int mid=l+r>>1;
build(lson,l,mid); build(rson,mid+1,r);
}
void push_down(int pos){
if (!tree[pos].lazy) return ;
if (tree[lson].dep!=0) {
if (tree[lson].dep%2==1) tree[lson].sum+=tree[pos].lazy;
else tree[lson].sum-=tree[pos].lazy;
}
if (tree[rson].dep!=0) {
if (tree[rson].dep%2==1) tree[rson].sum+=tree[pos].lazy;
else tree[rson].sum-=tree[pos].lazy;
}
tree[lson].lazy+=tree[pos].lazy;
tree[rson].lazy+=tree[pos].lazy;
tree[pos].lazy=0;
}
void change(int pos,int l,int r,int L,int R,int k){
if (l>=L && r<=R) {
tree[pos].lazy+=k;
if (tree[pos].dep!=0) {
if (tree[pos].dep%2) tree[pos].sum+=k;
else tree[pos].sum-=k;
}
return ;
}
int mid=l+r>>1; push_down(pos);
if(L<=mid) change(lson,l,mid,L,R,k);
if(R>mid) change(rson,mid+1,r,L,R,k);
}
int query(int pos,int l,int r,int k) {
if (l==r) return tree[pos].sum;
int mid=l+r>>1; push_down(pos);
if (k<=mid) return query(lson,l,mid,k);
else return query(rson,mid+1,r,k);
}
}
namespace sp{
void dfs1(int now,int Fa) {
size[now]=1; fa[now]=Fa; dep[now]=dep[Fa]+1;
for (int i=head[now];i;i=e[i].next) {
if (e[i].to==Fa) continue;
dfs1(e[i].to,now);
size[now]+=size[e[i].to];
if (size[son[now]]<size[e[i].to]) son[now]=e[i].to;
}
}
void dfs2(int now,int Top) {
dfn[now]=++tot; pre[tot]=now; top[now]=Top;
if(son[now]) dfs2(son[now],Top);
for (int i=head[now];i;i=e[i].next) {
if (e[i].to==son[now]||e[i].to==fa[now]) continue;
dfs2(e[i].to,e[i].to);
}
}
}
signed main(){
n=read(); m=read();
for (int i=1;i<=n;++i) {
a[i]=read();
}
for (int i=1;i<n;++i) {
int x=read(),y=read();
add(x,y); add(y,x);
}
sp::dfs1(1,0); sp::dfs2(1,1); ss::build(1,1,n);
int op,x,y;
for (int i=1;i<=m;++i) {
op=read();
if (op==1) {
x=read(),y=read();
if (dep[x]%2) ss::change(1,1,n,dfn[x],dfn[x]+size[x]-1,y);
else ss::change(1,1,n,dfn[x],dfn[x]+size[x]-1,-y);
//cout<<" "<<ss::tree[1].sum<<endl;
}
if (op==2) {
x=read();
print(ss::query(1,1,n,dfn[x]));
putchar('\n');
}
}
return 0;
}
洛谷P2344 [USACO11FEB] Generic Cow Protests G
2023.8.5
标签:动态规划,前缀和,树状数组。离散化。
动态规划题,划分区间,需要求方案数,考虑设计状态:
设
处理区间和可以使用前缀和解决,设
初始化,dp[0]=1。
转移方程:
时间复杂度为:
枚举
考虑新建一个序列
那么转移就变成了:
其中
每次转移后令
于是题目转化成了求区间和,单点修改的问题,可以用树状数组实现(或者说权值树状数组)。不过根据数据范围会发现
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+2023;
const int Mod=1e9+9;
int tot,sum[N],a[N],n,m,tree[N],dp[N],lsh[N],b[N];
int lowbit(int x) {
return (x&-x);
}
void update(int x,int y) {
while(x<=n) {
tree[x]+=y;
tree[x]%=Mod;
x+=lowbit(x);
}
}
int Sum(int x) {
int res=0;
while(x) {
res+=tree[x];
res%=Mod;
x-=lowbit(x);
}
return res;
}
signed main(){
cin>>n;
for (int i=1;i<=n;++i) {
cin>>a[i];
sum[i]=sum[i-1]+a[i];
//lsh[i]=sum[i];
}
for (int i=0;i<=n;++i) {
lsh[++tot]=sum[i];
}
sort(lsh+1,lsh+tot+1);
tot=unique(lsh+1,lsh+tot+1)-lsh-1;
for (int i=0;i<=n;++i) {
b[i]=lower_bound(lsh+1,lsh+tot+1,sum[i])-lsh;
}
dp[0]=1;
update(b[0],dp[0]);
for (int i=1;i<=n;++i) {
dp[i]=Sum(b[i]);
update(b[i],dp[i]);
}
cout<<dp[n];
return 0;
}
CF402E Strictly Positive Matrix
2023.8.6
标签:图论、矩阵、强联通分量。
图论建模题。将矩阵中
根据矩阵乘法的定义,对于矩阵
因此,只要存在
那么如果所有元素都能互相到达,整张图就是张强联通图,所有点都在同一个强联通分量里。 跑一个
(经过上述推导后,可以发现实际做法跟
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+48);
}
const int N=1e6+2023;
int ans,n,m,a[2021][2021],tot,sta[N],sc,dfn[N],low[N];
bool vis[2021];
void Tarjan(int now) {
dfn[now]=low[now]=++tot;
vis[now]=1;
sta[++sc]=now;
for (int i=1;i<=n;++i) {
if (!a[now][i]) continue;
if (!dfn[i]) {
Tarjan(i);
low[now]=min(low[now],low[i]);
}
else if(vis[i]) {
low[now]=min(low[now],dfn[i]);
}
}
if (dfn[now]==low[now]) {
++ans;
int t;
t=sta[sc--]; vis[t]=0;
while(now!=t) {
t=sta[sc--];
vis[t]=0;
}
}
}
signed main(){
n=read();
for (int i=1;i<=n;++i) {
for (int j=1;j<=n;++j) {
a[i][j]=read();
if (a[i][j]>0) a[i][j]=1;
else a[i][j]=0;
}
}
for (int i=1;i<=n;++i) {
if(!dfn[i]) {
Tarjan(i);
}
}
if (ans==1) {
cout<<"YES";
}
else cout<<"NO";
return 0;
}
洛谷P5025 [SNOI2017] 炸弹
2023.8.6
标签:图论,优化建图。
把每个炸弹看作点,每个炸弹向它能炸到的炸弹连一条有向边,然后统计一下每个点能到达多少个点即可,时间复杂度
这种做法很慢,需要连的边数很多,考虑优化。
考虑到每个炸弹所能炸到的一定是一个区间范围内的所有炸弹,那么可以考虑区间连边,用线段树优化。(qbxt老师没讲这个做法,回头再补一个这个做法吧。) 连边的时间复杂度为
考虑一个时间复杂度为线性的做法。
我们设
连边时
每个炸弹最终引爆的炸弹一定是一段区间,统计答案时记录每个点能到达编号的最大最小值即可(类似单调栈?)时间复杂度大致为
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+2023;
int n,m,a[N],x[N],r[N];
int L[N],R[N],ans;
const int Mod=1e9+7;
signed main(){
cin>>n;
for (int i=1;i<=n;++i) {
cin>>x[i]>>r[i];
L[i]=R[i]=i;
}
for (int i=2;i<=n;++i) {
while (L[i]>1&&x[i]-x[L[i]-1]<=r[i]) {
r[i]=max(r[i],r[L[i]-1]-(x[i]-x[L[i]-1]));
L[i]=L[L[i]-1];
}
}
for (int i=n-1;i>=1;--i) {
while(R[i]<n&&x[R[i]+1]-x[i]<=r[i]) {
L[i]=min(L[i],L[R[i]+1]);
R[i]=R[R[i]+1];
}
}
for (int i=1;i<=n;++i) {
ans+=(i*(R[i]-L[i]+1));
ans%=Mod;
}
cout<<ans;
return 0;
}
洛谷P4211 [LNOI2014] LCA
2023.8.10
标签:LCA、树剖、线段树。
暴力求出所有LCA的深度肯定是不行的,而且这个题目只需要知道LCA的深度,并不需要确切知道LCA是谁。考虑一种特别的 LCA 的深度的求法。
求
题目要求我们求出给定区间里各个点对的 LCA 的深度和,那么每次对区间里的各个点执行一次上述操作,然后统计答案即可。
实现可以用树剖实现。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+48);
}
const int N=1e6+2023;
const int Mod=201314;
int n,m,a[N],fa[N],head[N<<1],cnt;
int dep[N],dfn[N],pre[N],top[N],size[N],son[N],tot;
int root=1;
struct node{
int next,to,w;
}e[N<<1];
void add(int u,int v) {
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
namespace ss{
#define lson pos<<1
#define rson pos<<1|1
struct node{
int sum,len,lazy;
}tree[N<<2];
void build(int pos,int l,int r) {
tree[pos].len=r-l+1;
if(l==r) return ;
int mid=l+r>>1;
build(lson,l,mid); build(rson,mid+1,r);
}
void push_down(int pos) {
if (!tree[pos].lazy) return ;
tree[lson].lazy+=tree[pos].lazy;
tree[rson].lazy+=tree[pos].lazy;
tree[lson].sum+=tree[pos].lazy*tree[lson].len;
tree[rson].sum+=tree[pos].lazy*tree[rson].len;
tree[pos].lazy=0;
}
void change(int pos,int l,int r,int L,int R,int k) {
if (l>=L && r<=R) {
tree[pos].sum+=tree[pos].len*k;
tree[pos].sum%=Mod;
// cout<<" "<<tree[pos].sum<<" "<<tree[pos].len<<endl;
tree[pos].lazy+=k; tree[pos].lazy%=Mod;
return ;
}
int mid=l+r>>1; push_down(pos);
if (L<=mid) change(lson,l,mid,L,R,k);
if (R>mid) change(rson,mid+1,r,L,R,k);
tree[pos].sum=tree[lson].sum+tree[rson].sum;
}
int query(int pos,int l,int r,int L,int R) {
//cout<<" "<<pos<<" "<<L<<" "<<R<<endl;
if (l>=L && r<=R) return tree[pos].sum;
int mid=l+r>>1,res=0; push_down(pos);
if (L<=mid) res=query(lson,l,mid,L,R),res%=Mod;
if (R>mid) res+=query(rson,mid+1,r,L,R),res%=Mod;
return res;
}
}
namespace sp{
void dfs1(int now,int Fa) {
fa[now]=Fa; dep[now]=dep[Fa]+1; size[now]=1;
for (int i=head[now];i;i=e[i].next) {
if (e[i].to==Fa) continue;
dfs1(e[i].to,now);
size[now]+=size[e[i].to];
if (size[son[now]]<size[e[i].to]) son[now]=e[i].to;
}
}
void dfs2(int now,int Top) {
dfn[now]=++tot; pre[tot]=now; top[now]=Top;
//cout<<" "<<now<<" "<<dfn[now]<<"\n";
if(son[now]) dfs2(son[now],Top);
for (int i=head[now];i;i=e[i].next) {
if (e[i].to==fa[now]||e[i].to==son[now]) continue;
dfs2(e[i].to,e[i].to);
}
}
void change(int x,int y) {
while(top[x]^top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
ss::change(1,1,n,dfn[top[x]],dfn[x],1);
//cout<<" "<<ss::query(1,1,n,dfn[top[x]],dfn[x])<<endl;
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
ss::change(1,1,n,dfn[x],dfn[y],1);
}
int query(int x,int y) {
int res=0;
while(top[x]^top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
res+=ss::query(1,1,n,dfn[top[x]],dfn[x]);
res%=Mod;
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
res+=ss::query(1,1,n,dfn[x],dfn[y]);
return res%Mod;
}
}
int tt,ans[N];
struct nd{
int flag,x,y,i;
}q[N<<1];
bool cmp(nd x,nd y) {
return x.x<y.x;
}
signed main(){
n=read(); m=read();
for (int i=2;i<=n;++i) {
int x=read()+1;
add(x,i); add(i,x);
}
ss::build(1,1,n);
sp::dfs1(1,0); sp::dfs2(1,1);
for (int i=1;i<=m;++i) {
int l=read(),r=read()+1,z=read()+1;
q[++tt].flag=0; q[tt].i=i; q[tt].x=l; q[tt].y=z;
q[++tt].flag=1; q[tt].i=i; q[tt].x=r; q[tt].y=z;
}
sort(q+1,q+tt+1,cmp);
for (int i=1;i<=tt;++i) {
for (int j=q[i-1].x+1;j<=q[i].x;++j) {
sp::change(1,j);
}
if (!q[i].flag) ans[q[i].i]=-sp::query(1,q[i].y);
else {
ans[q[i].i]+=sp::query(1,q[i].y);
ans[q[i].i]=(ans[q[i].i]+Mod)%Mod;
//ans[q[i].i]%=Mod;
}
}
for (int i=1;i<=m;++i) {
cout<<ans[i]<<"\n";
}
return 0;
}
洛谷P2312 [NOIP2014 提高组] 解方程
2023.8.14
标签:数学。
一道比较老的题目,很符合早期CCF出题风格。
题目给的数据范围很有意思,
对于一个普通的一元n次多项式来说,它的求值需要经过
计算过程:
这是一个一元n次多项式:
我们把它转成如下形式:
我们从最里面的那个括号开始运算,一层一层向外扩展,就能在只经过
对于这道题来讲,使用这种算法进行判断可以在
一点小细节:
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int Mod=1e9+7;
inline int read() {
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-'),f%=Mod;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48),x%=Mod;
return f?-x:x;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+48);
}
const int N=1e6+2023;
int n,m,a[N],cnt,ans[N],sum;
bool check(int x) {
sum=0;
for (int i=n;i>=1;--i) {
sum=(a[i]+sum)*x%Mod;
}
sum=(sum+a[0])%Mod;
if (sum) return 0;
else return 1;
}
signed main(){
n=read(); m=read();
for (int i=0;i<=n;++i) {
a[i]=read();
}
for (int i=1;i<=m;++i) {
if (check(i)) {
ans[++cnt]=i;
}
}
cout<<cnt<<"\n";
for (int i=1;i<=cnt;++i) {
cout<<ans[i]<<"\n";
}
return 0;
}
CF1153D Serval and Rooted Tree
2023.8.16
标签:树形DP,记忆化搜索。
一个min-max类型的DP题。
对于min节点,我们希望它的所有子节点的最小值尽可能大。
对于max节点,我们希望它的所有子节点中最大值尽可能大。
换而言之,对于min节点,它的每个子节点都可能影响其值,而对于max节点,能影响它的值的只有最大的那个子节点。
有一个贪心的思想是,对于每个对根节点有贡献的叶子节点,我们尽可能把大的数放在上面。对答案没有贡献的点就放剩下的数。
对于一个点
设会影响根节点大小的叶子结点的数量为
那么最终答案就是
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
void print(int x) {
if (x<0) putchar('-'),x=-x;
if (x>9) print(x/10);
putchar(x%10+48);
}
const int N=1e6+2023;
int n,m,a[N],head[N],cnt;
struct node{
int next,to,w;
}e[N];
void add(int u,int v) {
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
int dfs(int now) {
int res=0;
if (!head[now]) { //如果当前节点为叶子节点
res=1; // 需要在这个点上填一个数。
++m;//叶子节点数量+1。
}
else if (a[now]) { //如果当前点取max
res=0x7ffffff;
for (int i=head[now];i;i=e[i].next) {
res=min(res,dfs(e[i].to));
}
}
else { // 如果当前点取min
res=0;
for (int i=head[now];i;i=e[i].next) {
res+=dfs(e[i].to);
}
}
return res;
}
signed main(){
n=read();
for (int i=1;i<=n;++i) {
a[i]=read();
}
for (int i=2;i<=n;++i) {
int x=read();
add(x,i);
}
int k=dfs(1);
cout<<m-k+1; //根节点最大值 = 叶子节点数量 - 需要填的数的数量 + 1
return 0;
}
洛谷P1742 最小圆覆盖
2023.8.22
标签:计算几何,随机化。
一道看上去很简单的题目。求n个点的最小圆覆盖。
根据数学知识,可以得知三点可以确定一个圆。(前提是三点不共线。)
根据三点坐标计算圆心和半径的公式这里就不展开了。
一个定理是:如果一个点不在所有点的最小覆盖圆内,那么这个点一定在所有点的最小覆盖圆上。
那么就可以想到一个
初始时选一个点为圆心,此时这个圆里只有这一个点,半径为
然后枚举其他的点,枚举到的点无非三种情况:
1.在当前所求的圆里面。
2.在当前所求圆的边境上。
3.在当前所求圆外。
前两种情况无需更新答案。当出现第三种情况的时候,我们用新枚举到的这个点和前面枚举过的两个点(这两个点需要再枚举一下)确定一个圆,同时判断一下是否合法(不合法的话继续枚举两个之前枚举过的点与这个新枚举到的点确定圆,直到合法为止。)。
最终枚举过所有点后所求出的圆心和半径即为答案。
然而这样做的时间复杂度是难以接受的,同时毒瘤出题人还可以搞出前三个点共线这种数据卡人。
因此可以使用一种算法:随机增量法。
由于三个点可以确定一个圆。因此对于一个随机的数列,在
而影响我们时间复杂度的是更新最小圆覆盖的次数。
因此进入第一重循环的if的概率(第一个点检测到合法的概率)为
进入第二重循环的if的概率为
而算上开启第二重循环的概率,可以得出在第二重循环所操作次数的期望为
因此,三重循环下来的实际时间复杂度是
代码如下:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+2023;
double eps=1e-9;
int n,m;
double X,Y,R;
struct node{
double x,y;
}e[N];
double dis(node b) {
return sqrt((X-b.x)*(X-b.x)+(Y-b.y)*(Y-b.y));
}
void Get_ans(node A,node B,node C){
double a=2*(A.x-B.x),b=2*(A.y-B.y),c=2*(B.x-C.x),d=2*(B.y-C.y);
double f=(A.x*A.x)-(B.x*B.x)+(A.y*A.y)-(B.y*B.y);
double g=(B.x*B.x)-(C.x*C.x)+(B.y*B.y)-(C.y*C.y);
X=(d*f-b*g)/(d*a-b*c); Y=(c*f-a*g)/(c*b-a*d);
R=sqrt((A.x-X)*(A.x-X)+(A.y-Y)*(A.y-Y));
}
void ss(){
X=e[1].x,Y=e[1].y; R=0;
for (int i=2;i<=n;++i) {
if (dis(e[i])>R+eps){
X=e[i].x,Y=e[i].y,R=0;
for (int j=1;j<=i-1;++j) {
if (dis(e[j])>R+eps) {
X=(e[i].x+e[j].x)/2; Y=(e[i].y+e[j].y)/2;
R=dis(e[j]);
for (int k=1;k<=j-1;++k) {
if (dis(e[k])>R+eps) {
Get_ans(e[i],e[j],e[k]);
}
}
}
}
}
}
}
signed main(){
cin>>n;
for (int i=1;i<=n;++i) {
cin>>e[i].x>>e[i].y;
}
random_shuffle(e+1,e+n+1); //随机打乱。
ss();
printf("%.10lf\n%.10lf %.10lf",R,X,Y);
return 0;
}
CF916E Jamie and Tree
2023.8.23
标签:树链剖分、LCA。
如果没有换根操作,这题就是道树剖裸题。加入换根操作后,我们肯定不能每换一次根就从头再遍历一次树。
先以
设根节点为
-
位于 ,那么使用原本树剖修改、查询 的子树的做法即可。 -
位于 的子树外,则对做法依旧无影响,使用原本树剖做法即可。 -
位于 的子树内,那么需要执行操作的点为:
从
先对整颗树进行操作,再找到
不过我们在执行操作的时候还会遇到一个问题:
原本求
1.如果
2.如果
3.如果
当
当
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+48);
}
const int N=2e5+2023;
int n,m,a[N],cnt,head[N];
int root=1,tot;
int pre[N],dfn[N],son[N],fa[N],size[N],top[N],dep[N];
struct node{
int next,to,w;
}e[N<<1];
void add(int u,int v) {
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
namespace ss{
#define lson pos<<1
#define rson pos<<1|1
struct node{
int sum,len,lazy;
}tree[N<<2];
void build(int pos,int l,int r) {
tree[pos].len=r-l+1;
if (l==r) {
tree[pos].sum=a[pre[l]];
return ;
}
int mid=l+r>>1;
build(lson,l,mid); build(rson,mid+1,r);
tree[pos].sum=tree[lson].sum+tree[rson].sum;
}
void push_down(int pos) {
if (!tree[pos].lazy) return ;
tree[lson].lazy+=tree[pos].lazy;
tree[rson].lazy+=tree[pos].lazy;
tree[lson].sum+=tree[pos].lazy*tree[lson].len;
tree[rson].sum+=tree[pos].lazy*tree[rson].len;
tree[pos].lazy=0;
}
void change(int pos,int l,int r,int L,int R,int k) {
if (l>=L && r<=R) {
tree[pos].sum+=k*tree[pos].len;
tree[pos].lazy+=k;
return ;
}
int mid=l+r>>1; push_down(pos);
if (L<=mid) change(lson,l,mid,L,R,k);
if (R>mid) change(rson,mid+1,r,L,R,k);
tree[pos].sum=tree[lson].sum+tree[rson].sum;
}
int query(int pos,int l,int r,int L,int R) {
if (l>=L && r<=R) return tree[pos].sum;
int mid=l+r>>1,res=0; push_down(pos);
if (L<=mid) res+=query(lson,l,mid,L,R);
if (R>mid) res+=query(rson,mid+1,r,L,R);
return res;
}
}
namespace sp{
void dfs1(int now,int Fa) {
fa[now]=Fa; dep[now]=dep[Fa]+1; size[now]=1;
for (int i=head[now];i;i=e[i].next) {
if (e[i].to==Fa) continue;
dfs1(e[i].to,now);
size[now]+=size[e[i].to];
if (size[e[i].to]>size[son[now]]) son[now]=e[i].to;
}
}
void dfs2(int now,int Top) {
dfn[now]=++tot; pre[tot]=now; top[now]=Top;
if (son[now]) dfs2(son[now],Top);
for (int i=head[now];i;i=e[i].next) {
if (e[i].to==fa[now]||e[i].to==son[now]) continue;
dfs2(e[i].to,e[i].to);
}
}
int lca(int x,int y) {
while(top[x]^top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
return x;
}
int LCA(int x,int y) {
if (dep[x]>dep[y]) swap(x,y);
int A=lca(x,root),B=lca(y,root),C=lca(x,y);
//cout<<" "<<A<<" "<<B<<" "<<C<<endl;
if (C==x) {
if (A==x) {
if (B==y) return y;
else return B;
}
return x;
}
if (A==x) return x;
if (B==y) return y;
if ((A==root&&B==C)||(B==root&&A==C)) return root;
if (A==B) return C;
if (C!=A) return A;
else return B;
}
int find(int x,int y) {
while(top[x]!=top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
if (fa[top[x]]==y) return top[x];
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
return son[x];
}
void update(int x,int k) {
if (x==root) {
ss::change(1,1,n,1,n,k);
return ;
}
if (dfn[root]>=dfn[x]&&dfn[root]<=dfn[x]+size[x]-1) {
// ss::change(1,1,n,dfn[x],dfn[x]+size[x]-1,k);
ss::change(1,1,n,1,n,k);
int y=find(x,root);
ss::change(1,1,n,dfn[y],dfn[y]+size[y]-1,-k);
}
else ss::change(1,1,n,dfn[x],dfn[x]+size[x]-1,k);
}
int query(int x) {
if (x==root){
return ss::query(1,1,n,1,n);
}
if (dfn[root]>=dfn[x]&&dfn[root]<=dfn[x]+size[x]-1) { //root在x子树内
int res=0;
//res+=ss::query(1,1,n,dfn[x],dfn[x]+size[x]-1);
res+=ss::query(1,1,n,1,n);
int y=find(x,root);
res-=ss::query(1,1,n,dfn[y],dfn[y]+size[y]-1);
return res;
}
return ss::query(1,1,n,dfn[x],dfn[x]+size[x]-1);
}
}
signed main(){
n=read(); m=read();
for (int i=1;i<=n;++i) {
a[i]=read();
}
for (int i=1;i<n;++i) {
int x=read(),y=read();
add(x,y); add(y,x);
}
root=1;
sp::dfs1(root,0); sp::dfs2(root,root); ss::build(1,1,n);
//for (int i=1;i<=n;++i) {
// cout<<" "<<dfn[i]<<"\n";
//
for (int i=1;i<=m;++i) {
int op=read();
if (op==1) root=read();
else if (op==2) {
int x=read(),y=read(),z=read();
sp::update(sp::LCA(x,y),z);
}
else if (op==3) {
int x=read();
print(sp::query(x));
putchar('\n');
}
}
return 0;
}
ABC318D General Weighted Max Matching
2023.9.3
标签:状压DP
看到
设
设
转移方程:
注:
枚举状态时间复杂度为
需要开 long long
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+2023;
int n,m,a[102][102];
int dp[N],ans;
// 设 S 表示已选的集合。dp[s]表示选择集合为 S 时的最大收益
signed main(){
cin>>n;
for (int i=0;i<n;++i) {
for (int j=i+1;j<n;++j) {
cin>>a[i][j];
}
}
for (int i=0;i<(1<<n)-1;++i) {
int w=-1;
for (int j=0;j<n;++j) {
if (!(i>>j&1)) {
w=j;
break;
}
}
for (int j=0;j<n;++j) {
if (!(i>>j&1)) {
int s=i|(1<<w)|(1<<j);
dp[s]=max(dp[s],dp[i]+a[w][j]);
ans=max(ans,dp[s]);
}
}
}
cout<<ans;
return 0;
}
洛谷P3469 [POI2008] BLO-Blockade
2023.9.9
标签:tarjan,割点.
总点数设为
题目要我们求封锁一个点后的不连通点对(有顺序性,
也就是说,即使点
设当前点为
则每个连通块对于
注意:
所以答案为:
非割点的情况答案为
假设点
时间复杂度为
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
const int N=1e6+2023;
int n,m,head[N<<1],cnt,tot;
int low[N],dfn[N],size[N],ans[N];
// size[now] 表示 在dfs树上,now 的子树大小
// ans[i] 表示封锁第i个点后的答案。
struct node{
int next,to,w;
}e[N<<1];
void add(int u,int v) {
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void Tarjan(int now) {
dfn[now]=low[now]=++tot;
int t=n-1;
//t表示除去now及其子树的连通块大小,初始视为n-1 。
size[now]=1; ans[now]=t;
for (int i=head[now];i;i=e[i].next) {
if (!dfn[e[i].to]) {
Tarjan(e[i].to);
size[now]+=size[e[i].to];
low[now]=min(low[now],low[e[i].to]);
if (low[e[i].to]>=dfn[now]) {
// now为割点,统计其子树e[i].to对答案的贡献。
ans[now]+=size[e[i].to]*(n-size[e[i].to]);
t-=size[e[i].to];
}
}
else {
low[now]=min(low[now],dfn[e[i].to]);
}
}
ans[now]+=t*(n-t);
//除去now及其子树外,还有一个大小为t的连通块。
}
signed main(){
n=read(); m=read();
for (int i=1;i<=m;++i) {
int x=read(),y=read();
add(x,y); add(y,x);
}
Tarjan(1);
for (int i=1;i<=n;++i)
cout<<ans[i]<<"\n";
return 0;
}
洛谷P5058 [ZJOI2004] 嗅探器
2023.9.13
标签:tarjan,双连通分量。
先考虑无解的情况,点
考虑有解的情况,不难发现,我们安装嗅探器的点一定是个割点,因此我们可以使用
接下来,我们考虑这些割点是否适合安装嗅探器。可以想到,不在
如何判断一个割点是否在
在从点
代码实现时,可以在tarjan过程中,找到一个点
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+2023;
inline int read() {
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
bool vis[N];
int n,m,cnt,head[N<<1],tot,dfn[N],low[N],a,b;
struct node{
int next,to;
}e[N<<1];
void add(int u,int v) {
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void Tarjan(int now) {
dfn[now]=low[now]=++tot;
for (int i=head[now];i;i=e[i].next) {
if (!dfn[e[i].to]) {
Tarjan(e[i].to);
low[now]=min(low[now],low[e[i].to]);
if (low[e[i].to]>=dfn[now]&&dfn[e[i].to]<=dfn[b]&&now!=a) {
vis[now]=1;
}
}
else {
low[now]=min(low[now],dfn[e[i].to]);
}
}
}
signed main(){
n=read();
while(1) {
int x=read(),y=read();
if (x==0&&y==0) break;
add(x,y); add(y,x);
}
a=read(); b=read();
Tarjan(a);
for (int i=1;i<=n;++i) {
if (vis[i]) {
cout<<i;
return 0;
}
}
cout<<"No solution";
return 0;
}
洛谷P2824 [HEOI2016/TJOI2016] 排序
2023.10.7
标签:线段树。
正常对一个序列进行排序需要
我们可以用线段树维护序列的值,以及区间里1的个数。
设
升序排序:
降序排序:
特别注意:当
这样我们就可以实现
现在考虑如何将原序列转化为01序列。
由于原题只需要执行一次询问,因此考虑一个离线做法。
我们二分位置
每次我们把原序列里所有大于等于
然后执行排序操作,若所有排序操作结束后,位置
最后得到的合法
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
void print(int x) {
if (x<0) putchar('-'),x=-x;
if (x>9) print(x/10);
putchar(x%10+48);
}
const int N=1e6+2023;
int n,m,a[N],op[N],L[N],R[N],p;
namespace ss{
#define lson pos<<1
#define rson pos<<1|1
struct node{
int cnt1,lazy,len;
}tree[N<<4];
void build(int pos,int l,int r,int x) {
tree[pos].len=r-l+1; tree[pos].lazy=-1;
tree[pos].cnt1=0;
if (l==r) {
tree[pos].cnt1=(a[l]>=x);
tree[pos].lazy=-1;
return ;
}
int mid=l+r>>1;
build(lson,l,mid,x); build(rson,mid+1,r,x);
tree[pos].cnt1=tree[lson].cnt1+tree[rson].cnt1;
}
void push_down(int pos) {
if (tree[pos].lazy==-1) return ;
tree[lson].lazy=tree[pos].lazy;
tree[rson].lazy=tree[pos].lazy;
if (tree[pos].lazy==1) {
tree[lson].cnt1=tree[lson].len;
tree[rson].cnt1=tree[rson].len;
}
else tree[lson].cnt1=tree[rson].cnt1=0;
//tree[lson].cnt1=tree[lson].len*tree[pos].lazy;
//tree[rson].cnt1=tree[rson].len*tree[pos].lazy;
tree[pos].lazy=-1; return ;
}
void change(int pos,int l,int r,int L,int R,int k) {
if (l>=L && r<=R) {
tree[pos].cnt1=k*tree[pos].len;
tree[pos].lazy=k; return ;
}
if (L>r||R<l) return ;
int mid=l+r>>1; push_down(pos);
if (L<=mid) change(lson,l,mid,L,R,k);
if (R>mid) change(rson,mid+1,r,L,R,k);
tree[pos].cnt1=tree[lson].cnt1+tree[rson].cnt1;
}
int query(int pos,int l,int r,int L,int R) {
if (l>=L && r<=R) return tree[pos].cnt1;
if (L>r||R<l) return 0;
int mid=l+r>>1; push_down(pos);
return query(lson,l,mid,L,R)+query(rson,mid+1,r,L,R);
}
int x_query(int pos,int l,int r,int x) {
if (l==r) return tree[pos].cnt1;
int mid=l+r>>1; push_down(pos);
if (x<=mid) return x_query(lson,l,mid,x);
else return x_query(rson,mid+1,r,x);
}
}
bool check(int x) {
ss::build(1,1,n,x);
for (int i=1;i<=m;++i) {
int cnt1=ss::query(1,1,n,L[i],R[i]);
if (op[i]==0) {
ss::change(1,1,n,R[i]-cnt1+1,R[i],1);
ss::change(1,1,n,L[i],R[i]-cnt1,0);
}
else {
ss::change(1,1,n,L[i],L[i]+cnt1-1,1);
ss::change(1,1,n,L[i]+cnt1,R[i],0);
}
}
return ss::x_query(1,1,n,p);
}
signed main(){
n=read(); m=read();
for (int i=1;i<=n;++i) {
a[i]=read();
}
for (int i=1;i<=m;++i) {
op[i]=read(); L[i]=read(); R[i]=read();
}
p=read();
int l=1,r=n,mid,ans;
while(l<=r) {
mid=l+r>>1;
if (check(mid)) {
ans=mid;
l=mid+1;
}
else r=mid-1;
}
cout<<ans;
return 0;
}
CF1083C Max Mex
2023.10.8
标签:线段树,LCA。
我们设
我们用线段树对
对于线段树上的一个区间
如果不存在一个
设
线段树上所有叶子节点的
再考虑区间合并。
设两个相邻区间分别为
当
判断三点位于同一条路径上的方法:
设三点间的距离为
树上快速求两点间距离的方法:
设两点分别为
注意: 倍增求LCA在本题可能会被卡常,建议使用树剖求LCA。
询问答案时可以采用二分,但如果直接用二分+线段树的话时带两个
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
void print(int x) {
if (x<0) putchar('-'),x=-x;
if (x>9) print(x/10);
putchar(x%10+48);
}
const int N=1e6+2023;
int n,m,a[N],head[N],cnt,dep[N],fa[N],son[N],size[N],top[N],tot;
int num[N];
struct nd{
int next,to;
}e[N];
void add(int u,int v) {
e[++cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
namespace sp{
void dfs1(int now,int Fa) {
size[now]=1; fa[now]=Fa; dep[now]=dep[Fa]+1;
for (int i=head[now];i;i=e[i].next) {
if (e[i].to==Fa) continue;
dfs1(e[i].to,now);
size[now]+=size[e[i].to];
if (size[e[i].to]>size[son[now]]) son[now]=e[i].to;
}
}
void dfs2(int now,int Top) {
top[now]=Top;
if (son[now]) dfs2(son[now],Top);
for (int i=head[now];i;i=e[i].next) {
if (e[i].to==fa[now]||e[i].to==son[now]) continue;
dfs2(e[i].to,e[i].to);
}
}
int LCA(int x,int y) {
while(top[x]^top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
return x;
}
}
struct node{
int x,y;
}tree[N<<2];
int JL(int x,int y) {
int lca=sp::LCA(x,y);
//cout<<" "<<x<<" "<<y<<" "<<lca<<endl;
return dep[x]+dep[y]-2*dep[lca];
}
node hb(node a,int C) {
if (a.x<0||C<0) return (node){-1,-1};
int A=a.x,B=a.y;
int d1=JL(A,C); int d2=JL(B,C); int d3=JL(A,B);
if (d1+d2==d3) return (node){A,B};
if (d1+d3==d2) return (node){C,B};
if (d2+d3==d1) return (node){A,C};
return (node){-1,-1};
}
namespace ss{
#define lson pos<<1
#define rson pos<<1|1
node Merge(node a,node b) {
if (a.x<0||b.x<0) return (node){-1,-1};
node s=hb(a,b.x);
if (s.x<0) return (node){-1,-1};
else return hb(s,b.y);
}
void push_up(int pos) {
tree[pos]=Merge(tree[lson],tree[rson]);
}
void build(int pos,int l,int r) {
if (l==r) {
tree[pos].x=num[l];
tree[pos].y=num[l];
return ;
}
int mid=l+r>>1;
build(lson,l,mid); build(rson,mid+1,r);
push_up(pos);
}
void change(int pos,int l,int r,int x) {
if (l==r) {
tree[pos].x=num[l];
tree[pos].y=num[l];
return ;
}
int mid=l+r>>1;
if (x<=mid) change(lson,l,mid,x);
else change(rson,mid+1,r,x);
push_up(pos);
}
int query(node &now,int pos,int l,int r) {
if (tree[pos].x>=0) {
if (now.x<0) {
now=tree[pos];
return r+1;
}
node s=Merge(now,tree[pos]);
if (s.x!=-1) {
now=s;
return r+1;
}
}
if (l==r) return l;
int mid=l+r>>1,res;
res=query(now,lson,l,mid);
if (res<=mid) return res;
else return query(now,rson,mid+1,r);
}
}
signed main(){
n=read();
for (int i=1;i<=n;++i) {
a[i]=read()+1;
num[a[i]]=i;
}
for (int i=2;i<=n;++i) {
int x=read();
add(i,x); add(x,i);
}
sp::dfs1(1,0);
sp::dfs2(1,1);
ss::build(1,1,n);
m=read();
while(m--) {
int op=read();
if (op==1) {
int l=read(),r=read();
swap(num[a[l]],num[a[r]]);
swap(a[l],a[r]);
ss::change(1,1,n,a[l]);
ss::change(1,1,n,a[r]);
}
else {
node s=(node){-1,-1};
int ans=ss::query(s,1,1,n);
cout<<ans-1<<"\n";
}
}
return 0;
}
CF689D Friends and Subsequences
2023.10.8
标签:线段树,二分。
注:由于本题是不带修改的静态查询,因此用ST表代替线段树可以优化成一个
不难想到使用线段树维护区间最大值和最小值,但
可以发现,如果我们固定住
因此可以得出结论:对于一个确定的
我们可以二分求出每个
可以采用在线段树上二分的方法,对于每个区间采用:
递归左子树-> 返回答案 -> 递归右子树的方法查询区间
具体实现:
固定
设
同理设
用全局变量
用
再从
最终时间复杂度约为
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?-x:x;
}
void print(int x) {
if (x<0) putchar('-'),x=-x;
if (x>9) print(x/10);
putchar(x%10+48);
}
const int INF=0x7fffffff;
const int N=1e6+2023;
int n,m,a[N],b[N],maxx,minn;
namespace ss{
#define lson pos<<1
#define rson pos<<1|1
struct node{
int maxx,minn;
}tree[N<<2];
void push_up(int pos) {
tree[pos].maxx=max(tree[lson].maxx,tree[rson].maxx);
tree[pos].minn=min(tree[lson].minn,tree[rson].minn);
}
void build(int pos,int l,int r) {
if (l==r) {
tree[pos].maxx=a[l];
tree[pos].minn=b[l];
return ;
}
int mid=l+r>>1;
build(lson,l,mid); build(rson,mid+1,r);
push_up(pos);
}
int query1(int pos,int l,int r,int L,int R) {
int mid=l+r>>1;
if (l>=L && r<=R) {
int tmax=max(maxx,tree[pos].maxx);
int tmin=min(minn,tree[pos].minn);
if (tmax<=tmin) {
maxx=tmax;
minn=tmin;
return r+1;
}
if (l==r) return l;
int now=query1(lson,l,mid,L,R);
if (now==mid+1) now=query1(rson,mid+1,r,L,R);
return now;
}
else if (mid>=L && mid<R){
int now=query1(lson,l,mid,L,R);
if (now==mid+1) now=query1(rson,mid+1,r,L,R);
return now;
}
else if (mid>=R) return query1(lson,l,mid,L,R);
else return query1(rson,mid+1,r,L,R);
}
int query2(int pos,int l,int r,int L,int R) {
int mid=l+r>>1;
if (l>=L && r<=R) {
int tmax=max(maxx,tree[pos].maxx);
int tmin=min(minn,tree[pos].minn);
if (tmax<tmin) {
maxx=tmax;
minn=tmin;
return r;
}
if (l==r) return l-1;
int now=query2(lson,l,mid,L,R);
if (now==mid) now=query2(rson,mid+1,r,L,R);
return now;
}
else if (mid>=L && mid<R) {
int now=query2(lson,l,mid,L,R);
if (now==mid) now=query2(rson,mid+1,r,L,R);
return now;
}
else if (R<=mid) return query2(lson,l,mid,L,R);
else return query2(rson,mid+1,r,L,R);
}
}
signed main(){
n=read();
for (int i=1;i<=n;++i) {
a[i]=read();
}
for (int i=1;i<=n;++i) {
b[i]=read();
}
ss::build(1,1,n);
int ans=0;
for (int i=1;i<=n;++i) {
maxx=-INF,minn=INF;
int R=ss::query1(1,1,n,i,n);
maxx=-INF,minn=INF;
int L=ss::query2(1,1,n,i,n);
ans+=R-L-1;
}
cout<<ans;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧