Codeforces上几个非常妙的数据结构题

Codeforces 833B The Bakery

第一次做线段树优化dp.没想到div.2的D这么难,以后真的要退竞了.
将一串数字分为k个连续区间,使得每一段中出现数字种数的总和最大.
我们先来思考一下裸的dp.
定义dp[i][j]为前 i 1 个数分割为 j 份的最大值.
因此有代码

dp[i][j]=max(dp[k=1 to i][j-1])+sum(k,n);
/*此处的sum(k,n)是k to n出现数字的总数.*/
这个sum(k,n)不好处理,如果要强行预处理要n^2的复杂度,不能支持.

对于一个数a[i]来说,我们需要知道上一个a[i]出现在哪里.
所以我们要处理一个 p r e 数组.
所以有如下代码.

for (i=1;i<=n;++i){
  scanf("%d",&a[i]);
  pre[i]=pos[a[i]]+1;
  pos[a[i]]=i;
  }
/*应该可以看懂吧.*/

对于a[i]来说,它只能在 p r e [ i ] i 这段区间内产生1的贡献.(否则与前面出现了重复的数字没有贡献.)
那么用线段树维护区间最大值即可.
ps:一不小心把dp数组打反了re在第8个点.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
inline void read(rel &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  x=f?x:-x;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=3.5e4;
typedef int fuko[yuzu|10];
typedef int karen[yuzu<<2|13];
int n=read(),k=read();
fuko a,dp[55],pre,pos;

struct segtree{
#define le rt<<1
#define ri le|1
#define ls le,l,mid
#define rs ri,mid+1,r
karen da,lazy;
void init(){
  memset(da,0,sizeof da);
  memset(lazy,0,sizeof lazy);
  }
void build(int d,int rt=1,int l=1,int r=n){
  if (l==r) da[rt]=dp[d][l-1];
  else{
    int mid=l+r>>1;
    build(d,ls),build(d,rs);
    da[rt]=max(da[le],da[ri]);
    }
  }
void push_down(int rt,int l,int r){
  if (lazy[rt]){
    da[le]+=lazy[rt],da[ri]+=lazy[rt];
    lazy[le]+=lazy[rt],lazy[ri]+=lazy[rt];
    lazy[rt]=0;
    }
  }
void update(int ql,int qr,int v,int rt=1,int l=1,int r=n){
  if (ql>r||qr<l) return;
  if (ql<=l&&qr>=r){
    da[rt]+=v,lazy[rt]+=v;
    }else{
    int mid=l+r>>1;
    push_down(rt,l,r);
    update(ql,qr,v,ls),update(ql,qr,v,rs);
    da[rt]=max(da[le],da[ri]);
    }
  }
int query(int ql,int qr,int rt=1,int l=1,int r=n){
  if (ql>r||qr<l) return 0;
  if (ql<=l&&qr>=r) return da[rt];
  int mid=l+r>>1;
  push_down(rt,l,r);
  return max(query(ql,qr,ls),query(ql,qr,rs));
  }
}my_;

int main(){
re0 i,j;
for (i=1;i<=n;++i){
  a[i]=read();
  pre[i]=pos[a[i]]+1;
  pos[a[i]]=i;
  }
for (i=1;i<=k;++i){
  my_.init();
  my_.build(i-1);
  for (j=1;j<=n;++j){
    my_.update(pre[j],j,1);
    dp[i][j]=my_.query(1,j);
    }
  }
write(dp[k][n]);
}

Codeforces 633H Fibonacci-ish II

q个询问,每次询问求[l,r]内所有a[i]从小到大排序并去重之后所有F(a[i])的和.
F(a[i])=斐波那契数列的第a[i]项.

本题标算用线段树维护莫队的add和del函数,复杂度 O ( n × n × l o g ( n ) ) ,常数巨大,有时候跑不过 O ( n × q ) 的暴力.
暴力 4100 m s 爆c过去.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
#define yi first
#define er second
typedef pair<int,int> pii;
const int yuzu=3e4;
typedef int fuko[yuzu|10];
fuko ans,step,fib={1,1},l,r,last;
int n=read(),m=read();
pii a[yuzu|10];

int main(){
int i,j;
for (i=1;i<=n;++i) a[i]=pii(read(),i);
sort(a+1,a+n+1);
memset(last,-1,sizeof last);
for (i=2;i<=n;++i) fib[i]=(fib[i-1]+fib[i-2])%m;
int q=read();
for (i=1;i<=q;++i) l[i]=read(),r[i]=read();
for (i=1;i<=n;++i){
  int d=a[i].yi%m;
  for (j=1;j<=q;++j){
    if (a[i].er>=l[j]&&a[i].er<=r[j]&&a[i].yi^last[j]){
      ans[j]=(ans[j]+fib[step[j]++]*d)%m;
      last[j]=a[i].yi;
      }  
    }
  }
for (i=1;i<=q;++i) write(ans[i]),pl;
}

Codeforces 522D Closest Equals

q个询问,每次询问区间[l,r]中相距最近的两个相同的数的距离.没有输出-1.
这题非常神.
首先你需要会处理每个数和在它前面离它最近的相同数之间的距离.这个以下几句代码就搞定了.

for (int i=1;i<=n;++i){
/*非常古老的操作了,用过很多遍,大家一定要记下来.注意这个pos数组它是个map.当然你也可以先离散化,但是写起来很难过.*/
  int x;scanf("%d",&x);
  pre[i]=pos[x];
  ord[pos[x]]=i;
  pos[x]=i;
  v[i]=!pre[i]?inf:i-pre[i];//如果没有前驱的话就设为inf.
  }

然后用线段树维护v数组区间的最小值即可.
但是还没有这么简单.如果询问出的答案是由 i p r e [ i ] 得到的但 p r e [ i ] l 的左边,岂不是就错了?
这时不要忘了只有询问.
我们把询问离线,按照左端点排序,这样在询问的时候就可以把 1 l 之间的所有 p r e [ i ] < l 的部分都更新为inf,就能够防止出现上面的情况了.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=5e5,inf=0x3f3f3f3f;
typedef int fuko[yuzu|10];
fuko a,pre,x,ans,ord;
map<int,int> pos;
int n=read(),m=read();

struct query{
int l,r,id;
bool operator <(const query &b) const{
  return l<b.l;
  }
}q[yuzu|10];

typedef int karen[yuzu<<2|13];
struct segtree{
#define le rt<<1
#define ri le|1
#define ls le,l,mid
#define rs ri,mid+1,r
karen xiao;
void build(int rt=1,int l=1,int r=n){
  if (l==r) xiao[rt]=x[l];
  else{
    int mid=l+r>>1;
    build(ls),build(rs);
    xiao[rt]=min(xiao[le],xiao[ri]);
    }
  }
void update(int u,int v,int rt=1,int l=1,int r=n){
  if (u>r||u<l) return;
  if (l==r) xiao[rt]=v;
  else{
    int mid=l+r>>1;
    update(u,v,ls),update(u,v,rs);
    xiao[rt]=min(xiao[le],xiao[ri]);
    }
  }
int query(int ql,int qr,int rt=1,int l=1,int r=n){
  if (ql>r||qr<l) return inf;
  if (ql<=l&&qr>=r) return xiao[rt];
  int mid=l+r>>1;
  return min(query(ql,qr,ls),query(ql,qr,rs));
  }
}my_;

int main(){
int i;
for (i=1;i<=n;++i){
  a[i]=read(); 
  pre[i]=pos[a[i]];
  ord[pos[a[i]]]=i;
  pos[a[i]]=i;
  x[i]=!pre[i]?inf:i-pre[i];
  }
my_.build();
for (i=1;i<=m;++i){
  q[i].l=read(),q[i].r=read(),q[i].id=i;
  }
sort(q+1,q+m+1);
int now=1;
for (i=1;i<=m;++i){
  for (;now<q[i].l;my_.update(ord[now++],inf));
  int tmp=my_.query(1,q[i].r);
  ans[q[i].id]=tmp^inf?tmp:-1;
  }
for (i=1;i<=m;++i) write(ans[i]),pl;
}  

Codeforces 718C Sasha and Array

我要给这题的出题人打 c a l l !这题出得太妙了!
维护区间加,区间F(a[i])和mod(10^9+7).F(x)是第x个斐波那契数.
首先,我们思考一下人生.求第x个斐波那契数模 10 9 + 7 的值可以用矩阵快速幂.
再看怎么处理 F ( x ) F ( x + v ) ,明显可以得到以下的式子.

令斐波那契数列的转移矩阵为A.
则有F(x)*(A^v)=F(x+v).

接下来问题已经迎刃而解.
我们用普通的维护区间和的线段树,不过这一次节点里储存的 v a l l a z y 不再是数字,而是矩阵.
区间的更新就用矩阵乘法即可.
能想到线段树维护矩阵快速幂,出题人非常厉害,不得不膜拜一下.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=1e5,mod=1e9+7;
typedef int fuko[yuzu|10];
int n=read(),m=read();
#define qing0(x) memset(x,0,sizeof x)

struct juzhen{
ll m[3][3];
void clearit(){
  qing0(m);
  for (int i=1;i<=2;++i) m[i][i]=1;
  }
bool judge(){
  return m[1][1]==1&&!m[1][2]&&!m[2][1]&&m[2][2]==1;
  }
juzhen operator *(const juzhen &a){
  int i,j,k;
  juzhen ans;
  qing0(ans.m);
  for (i=1;i<=2;++i){
    for (j=1;j<=2;++j){
      for (k=1;k<=2;++k){
        ans.m[i][j]=(ans.m[i][j]+m[i][k]*a.m[k][j])%mod;
        }
      }
    }return ans;
  }
juzhen operator +(const juzhen &b){
  int i,j;
  juzhen ans;
  qing0(ans.m);
  for (i=1;i<=2;++i){
    for (j=1;j<=2;++j){
      ans.m[i][j]=(ans.m[i][j]+m[i][j]+b.m[i][j])%mod;
      }
    }return ans;    
  }
void print(){
  int i,j;
  for (i=1;i<=2;++i,pl){
    for (j=1;j<=2;++j,p32) write(m[i][j]);
    }
  }
}tmp,zero;

juzhen operator ^(juzhen a,ll b){
juzhen ans;ans.clearit();
for (;b;b>>=1,a=a*a) 
  if (b&1) ans=ans*a;
return ans;
}

typedef juzhen karen[yuzu<<2|13];
struct segtree{
#define le rt<<1
#define ri le|1
#define ls le,l,mid
#define rs ri,mid+1,r
karen val,lazy;
void push_up(int rt){
  val[rt]=val[le]+val[ri];
  }
void build(int rt=1,int l=1,int r=n){
  qing0(val[rt].m);
  lazy[rt].clearit();
  if (l==r){
    int x=read()-1;
    juzhen now=tmp^x;
    val[rt].m[1][1]=now.m[1][1];
    val[rt].m[1][2]=now.m[1][2];
    return;
    } 
  int mid=l+r>>1;
  build(ls),build(rs);
  push_up(rt);
  }
void push_down(int rt){
  if (!lazy[rt].judge()){
    val[le]=val[le]*lazy[rt];
    val[ri]=val[ri]*lazy[rt];
    lazy[le]=lazy[le]*lazy[rt];
    lazy[ri]=lazy[ri]*lazy[rt];
    lazy[rt].clearit();
    }
  }
void update(int ql,int qr,juzhen v,int rt=1,int l=1,int r=n){
  if (ql>r||qr<l) return;
  if (ql<=l&&qr>=r){
    val[rt]=val[rt]*v;
    lazy[rt]=lazy[rt]*v;
    }else{
    int mid=l+r>>1;
    push_down(rt);
    update(ql,qr,v,ls),update(ql,qr,v,rs);
    push_up(rt);
    }
  }
juzhen query(int ql,int qr,int rt=1,int l=1,int r=n){
  if (ql>r||qr<l) return zero;
  if (ql<=l&&qr>=r) return val[rt];
  int mid=l+r>>1;
  push_down(rt);
  return query(ql,qr,ls)+query(ql,qr,rs);
  }
void dfs(int rt=1,int l=1,int r=n){
  printf("#%d %d %d\n",rt,l,r);
  val[rt].print();
  int mid=l+r>>1;
  if (l^r) dfs(ls),dfs(rs);
  }
}my_;

int main(){
tmp.m[1][1]=tmp.m[1][2]=tmp.m[2][1]=1;
my_.build();
for (;m--;){
  int op=read(),l=read(),r=read();
  switch(op){
    case 1: my_.update(l,r,tmp^read()); break;
    case 2: write(my_.query(l,r).m[1][1]),pl; break;
    }
  }
}

猜你喜欢

转载自blog.csdn.net/qq_31908675/article/details/80234171