F F F 小石的妹子
题意:对于每个妹子有两个属性,小石进行多轮,每轮找出不被完全大于的那些人,赋值为
1
1
1,然后再从剩下的选取并赋值为
2
2
2,以此类推。
题解:二维偏序问题,但是我一开始逆序对数量即可判断,实际上不可。
我们把问题转换成,这次要选择的等级:
R
k
j
=
m
a
x
(
R
k
i
)
+
1
Rk_j=max(Rk_i)+1
Rkj=max(Rki)+1,并且
R
k
i
Rk_i
Rki一定严格大于
R
k
j
Rk_j
Rkj。并且一定存在之前的一个比自己严格大,(但不是所有的比自己大。
这是关键,好好体会。
二维偏序可以用,排序一维,另一维进行权值线段树即可。
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
typedef long long ll;
using namespace std;
const int maxn = 500050;
struct tree2{
tree2 *lson,*rson;
int x;
}dizhi[maxn<<1],*root=&dizhi[0];
int n,m,t=1,a[maxn];
void push_up(tree2 *tree){
tree->x=max(tree->lson->x,tree->rson->x);
}
void build(tree2 *tree,int l,int r){
if(l==r){
tree->x=0;
return ;
}
int mid=(l+r)>>1;
tree->lson=&dizhi[t++];
tree->rson=&dizhi[t++];
build(tree->lson,l,mid);
build(tree->rson,mid+1,r);
push_up(tree);
}
void change(tree2 *tree,int l,int r,int x,int d){
if(l==r){
tree->x=d;
return ;
}
int mid=(l+r)>>1;
if(x<=mid)change(tree->lson,l,mid,x,d);
else change(tree->rson,mid+1,r,x,d);
push_up(tree);
}
int query(tree2 *tree,int l,int r,int x,int y){
if(x<=l&&y>=r)return tree->x;
int mid=(l+r)>>1;
int t1=0,t2=0;
if(x<=mid)t1=query(tree->lson,l,mid,x,y);
if(y>mid)t2=query(tree->rson,mid+1,r,x,y);
return max(t1,t2);
}
struct node{
int x,y,index;
friend bool operator < (node a,node b){
if(a.x==b.x)return a.y>b.y;
return a.x>b.x;
}
}A[maxn];
int ans[maxn],lsh[maxn];
int main(){
cin>>n;
FOR(i,1,n){
scanf("%d%d",&A[i].x,&A[i].y);
A[i].index=i,lsh[i]=A[i].y;
}
sort(lsh+1,lsh+1+n);
sort(A+1,A+1+n);
FOR(i,1,n)A[i].y=lower_bound(lsh+1,lsh+1+n,A[i].y)-lsh;
build(root,1,n);
FOR(i,1,n){
int cnt=query(root,1,n,A[i].y+1,n)+1;
ans[A[i].index]=cnt;
change(root,1,n,A[i].y,cnt);
}
FOR(i,1,n)printf("%d\n",ans[i]);
}
H H H 小阳的贝壳
题意:支持区间加和区间
g
c
d
gcd
gcd查询的数据结构(中间还有个相邻差最大值。
前两个要求很简单,我们可以利用差分实现,对于差分序列的最大值就是相邻差最大值,区间加也就是差分序列的线段树上的单点的修改。
第三个要求是区间
g
c
d
gcd
gcd的维护。
首先我们可以知道区间
g
c
d
gcd
gcd是可以合并的,那么如何支持带修的区间
g
c
d
gcd
gcd维护呢?
对于
g
c
d
gcd
gcd的一个性质:
g
c
d
(
a
,
b
)
=
g
c
d
(
a
,
b
−
a
)
gcd(a,b)=gcd(a,b-a)
gcd(a,b)=gcd(a,b−a)
g
c
d
(
a
,
b
,
c
)
=
g
c
d
(
a
,
b
−
a
,
c
−
b
)
=
g
c
d
(
a
,
g
c
d
(
b
−
a
,
c
−
b
)
)
gcd(a,b,c)=gcd(a,b-a,c-b)=gcd(a,gcd(b-a,c-b))
gcd(a,b,c)=gcd(a,b−a,c−b)=gcd(a,gcd(b−a,c−b))
也就是区间
g
c
d
[
l
,
r
]
=
g
c
d
(
a
[
l
]
,
g
c
d
(
a
[
l
+
1
]
−
a
[
l
]
,
.
.
.
.
,
a
[
r
]
−
a
[
r
−
1
]
)
)
gcd[l,r]=gcd(a[l],gcd(a[l+1]-a[l],....,a[r]-a[r-1]))
gcd[l,r]=gcd(a[l],gcd(a[l+1]−a[l],....,a[r]−a[r−1]))
也就是维护一个差分序列的
g
c
d
gcd
gcd即可,对差分序列的单点修改,只会影响单点
g
c
d
gcd
gcd,区间
g
c
d
gcd
gcd支持单点修改。
#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define inf 0x3f3f3f
typedef long long ll;
using namespace std;
const int maxn=5e5+10;
struct tree2{
tree2 *lson,*rson;
ll x;
ll mx,mi,gcd;
}dizhi[maxn<<1],*root=&dizhi[0];
int n,m,t=1;
ll a[maxn],b[maxn];
void push_up(tree2 *tree){
tree->x=tree->lson->x+tree->rson->x;
tree->mx=max(tree->lson->mx,tree->rson->mx);
tree->mi=min(tree->lson->mi,tree->rson->mi);
tree->gcd=__gcd(tree->lson->gcd,tree->rson->gcd);
}
void build(tree2 *tree,int l,int r){
if(l==r){
tree->gcd=tree->mx=tree->mi=tree->x=a[l];
return ;
}
int mid=(l+r)>>1;
tree->lson=&dizhi[t++];
tree->rson=&dizhi[t++];
build(tree->lson,l,mid);
build(tree->rson,mid+1,r);
push_up(tree);
}
void change(tree2 *tree,int l,int r,int x,int d){
if(l==r){
tree->x+=d;
tree->mx+=d;
tree->mi+=d;
tree->gcd+=d;
return ;
}
int mid=(l+r)>>1;
if(x<=mid)change(tree->lson,l,mid,x,d);
else change(tree->rson,mid+1,r,x,d);
push_up(tree);
}
ll querysum(tree2 *tree,int l,int r,int x,int y){
if(x<=l&&y>=r)return tree->x;
int mid=(l+r)>>1;
ll t1=0,t2=0;
if(x<=mid)t1=querysum(tree->lson,l,mid,x,y);
if(y>mid)t2=querysum(tree->rson,mid+1,r,x,y);
return t1+t2;
}
ll querymax(tree2 *tree,int l,int r,int x,int y){
if(x<=l&&y>=r)return tree->mx;
int mid=(l+r)>>1;
ll t1=-inf,t2=-inf;
if(x<=mid)t1=querymax(tree->lson,l,mid,x,y);
if(y>mid)t2=querymax(tree->rson,mid+1,r,x,y);
return max(t1,t2);
}
ll querymin(tree2 *tree,int l,int r,int x,int y){
if(x<=l&&y>=r)return tree->mi;
int mid=(l+r)>>1;
ll t1=inf,t2=inf;
if(x<=mid)t1=querymin(tree->lson,l,mid,x,y);
if(y>mid)t2=querymin(tree->rson,mid+1,r,x,y);
return min(t1,t2);
}
ll querygcd(tree2 *tree,int l,int r,int x,int y){
if(x<=l&&y>=r)return tree->gcd;
int mid=(l+r)>>1;
ll t=0;
if(x<=mid)t=__gcd(t,querygcd(tree->lson,l,mid,x,y));
if(y>mid)t=__gcd(t,querygcd(tree->rson,mid+1,r,x,y));
return abs(t);
}
int main(){
cin>>n>>m;
FOR(i,1,n)scanf("%lld",&b[i]);
FOR(i,1,n)a[i]=b[i]-b[i-1];
build(root,1,n);
FOR(M,1,m){
int opt,L,R;scanf("%d%d%d",&opt,&L,&R);
if(opt==1){
ll d;scanf("%lld",&d);
change(root,1,n,L,d);
if(R+1<=n)change(root,1,n,R+1,-d);
}
if(opt==2){
if(L==R){puts("0");continue;}
ll tmp1=-querymin(root,1,n,L+1,R);
ll tmp2=querymax(root,1,n,L+1,R);
printf("%lld\n",max(tmp1,tmp2));
}
if(opt==3){
ll now=querysum(root,1,n,1,L);
if(L==R){printf("%lld\n",now);continue;}
printf("%lld\n",__gcd(now,abs(querygcd(root,1,n,L+1,R))));
}
}
}
J J J 小雨坐地铁
题意:有
m
m
m条地铁线,每条地铁线从一个站到另一个站有自己的花费,上车也有独立的花费。问从某个车站到另一个车站的最少花费。地铁线总共只有
500
500
500,车站总共只有
1000
1000
1000
题解:对于
m
m
m条地铁线,即在图上建立
m
m
m条地铁线,对于每个车站,在每条地铁线上都会建立镜像的一个点方便处理,还有一个原来的车站店,镜像点到原来的点没有花费,反之有上车费。
对于同一地铁线上的点自然是另一个花费直接建双向边即可。
这种建图类似于现实场景中的了十分巧妙。
题解:
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define inf 0x3f3f3f3f3f3f3f3f
typedef long long ll;
using namespace std;
const int maxm = 2e6+100;
const int maxn = 1e6+500;
struct Edge{
int from,to;ll dist;
Edge(){}
Edge(int _from,int _to,ll _dist):from(_from),to(_to),dist(_dist){}
friend bool operator < (Edge a,Edge b){
return a.dist<b.dist;
}
};
int n,m;
Edge ed[maxm];int he[maxn],ne[maxn],etop=1;
priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > >q;
ll d[maxn];bool vis[maxn];int st[maxn];
void insert(int u,int v,ll w){
ed[etop]=Edge(u,v,w);
ne[etop]=he[u];
he[u]=etop++;
}
ll dijsktra(int s,int t){
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
q.push(make_pair(d[s]=0,s));
while(!q.empty()){
int u=q.top().second;q.pop();
if(vis[u])continue;
vis[u]=true;
for(int i=he[u];i;i=ne[i]){
Edge& e=ed[i];
if(d[e.to]>d[u]+e.dist){
d[e.to]=d[u]+e.dist;
q.push(make_pair(d[e.to],e.to));
}
}
}
return d[t]==inf?-1:d[t];
}
int get(int i,int j){return i*n+j;}
int main(){
int s,t,a,b,p,x,tmp;
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&p);
for(int j=1;j<=p;j++){
scanf("%d",&st[j]);
insert(get(i,st[j]),st[j],0);
insert(st[j],get(i,st[j]),a);
}
for(int j=1;j<p;j++){
insert(get(i,st[j]),get(i,st[j+1]),b);
}
for(int j=2;j<=p;j++){
insert(get(i,st[j]),get(i,st[j-1]),b);
}
}
cout<<dijsktra(s,t)<<endl;
}