2019.4.23 两题——[ 树状数组 ][ 思路+多方案LIS ]

第一题是 exclaim 。

  可以枚举 c 。考虑 c3 % k =w ,想求 ( a+b2 )%k = w 的方案。

  枚举到 i 的时候把 i 当作 b 贡献一些 ( a , b ) 的方案数。已知这个 b ,发现它会对一个区间的 w 造成影响。因为 a<=b 的 a 是 [ 1 , b ] ,即这个 b 使得 [ 1+b2 , b+b2 ] 这个区间的方案加一。

  有 %k 的话,就是可能对 [ 0 , k-1 ] 这整个区间加了很多遍。这部分的贡献记一个值表示即可。

  把 i 当作 b 贡献完之后,把 i 当作 c 查一下答案即可。这样是 O( T*nlogn ) 的。应该过不了。但题解就是这样,标程也是这样。

  自己没有写。

第二题是 sanrd 。

  考虑求出 f[ i ] 表示 i 位置结尾的 LIS 长度,g[ i ] 表示 i 位置结尾的 LDS 长度。那么合法的全局 LIS 就是位置递增、值递增、f[ ] 也递增的一些位置。

  dp[ i ][ j ] 表示前 i 个位置,选了 j 这个数作为 LIS 的结尾,LDS 结尾最大是多少。可以通过值和 f[ ] 来判断当前数是否能加入 LIS 或 LDS 。

    可以再用一维 0/1 表示 LIS 是否达到目前所在的水准,即这片位置的 f[ ] (只看可能在全局 LIS 里的点,其 f[ ] 是非严格递增的)。

    没实现。大概思路就是这样。是 n2 的?

  题解的第一种做法可能和这个有些相近。但没有看懂。

  那么看第二种做法:

  LIS 和 LDS 只有一个位置相交的发现很重要。

  一个 LIS ,如果它各位置上的 f[ ] 加起来是总的 LDS 个数,那么它不合法。

  用树状数组求 LIS 的时候,如果有两个可以接的位置,它们 LIS 长度相同,且至今为止的 \( \sum f \) 不同,那么把接在这两个位置后面的情况都保留下来。做到最后,如果有解,就会出现两个不同的 \( \sum f \) ,一定有一个不等于总的 LDS 个数。

  实现就是在树状数组维护 max 的时候加一个 “如果长度相同,\( \sum f \) 不同,保留两个 \( \sum f \) ”;求每个位置的 f[ ] 可以用树状数组存下方案数,得到每个位置结尾的 LDS 方案、每个位置开头的 LDS 方案,乘一下即可。

  注意 f[ i ] 是经过 i 的全局 LDS 个数,如果经过 i 的 LDS 不是全局 LDS ,f[ i ] = 0 。

  找 LIS 方案可以给每个位置记录 s0 , p0 , s1 , p1 , s0/1 表示该位置结尾的 LDS 的两个 \( \sum f \) ,p0/1 表示两个方案对应的上一个位置。

    注意判断应该跳 p0 还是 p1 ;如果 s0 + “已跳过位置的 f[ ] ” 等于总的 LDS 个数的话,就跳到 p1 ,否则跳到 p0 。

    注意方案是模意义下进行的,记得加减要写 upt( ) 。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=5e5+5,mod=1e9+9;
int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;}

int n,a[N],b0[N],b[N],p0[N],p1[N],s0[N],s1[N],dp[N],h[N];
bool vis[N],vs[N];
struct Node{
  int dp,s0,p0,s1,p1;
  Node(int d=0,int s0=0,int p0=0,int s1=0,int p1=0):
    dp(d),s0(s0),p0(p0),s1(s1),p1(p1) {}
}f[N];
struct Dt{
  int dp,sm;
  Dt(int d=0,int s=0):dp(d),sm(s) {}
  Dt operator+ (const Dt &b)const
  {
    Dt ret=Dt(Mx(dp,b.dp),0);
    if(dp==ret.dp)ret.sm=sm;
    if(b.dp==ret.dp)ret.sm=upt(ret.sm+b.sm);
    return ret;
  }
}g[N];
void mrg(Node &u,Node v)
{
  if(v.dp>u.dp)u=v;
  else if(v.dp==u.dp&&!u.s1)
    {
      if(v.s0!=u.s0)
    { u.s1=v.s0;u.p1=v.p0;}
      else if(v.s1&&v.s1!=u.s0)
    { u.s1=v.s1;u.p1=v.p1;}
    }
}
Node c[N];
Dt qry1(int x){Dt ret;for(;x<=n;x+=(x&-x))ret=ret+g[x];return ret;}
void ins(int x,Dt d){for(;x;x-=(x&-x))g[x]=g[x]+d;}
Dt qry1x(int x){Dt ret;for(;x;x-=(x&-x))ret=ret+g[x];return ret;}
void insx(int x,Dt d){for(;x<=n;x+=(x&-x))g[x]=g[x]+d;}
Node qry2(int x){Node ret;for(;x;x-=(x&-x))mrg(ret,f[x]);return ret;}
void ins(int x,Node d){for(;x<=n;x+=(x&-x))mrg(f[x],d);}
int qry3(int x){int ret=0;for(;x<=n;x+=(x&-x))ret=Mx(ret,h[x]);return ret;}
void ins(int x,int d){for(;x;x-=(x&-x))h[x]=Mx(h[x],d);}
int main()
{
  freopen("sanrd.in","r",stdin);
  freopen("sanrd.out","w",stdout);
  n=rdn();
  for(int i=1;i<=n;i++)a[i]=rdn();
  for(int i=1;i<=n;i++)
    {
      Dt d=qry1(a[i]+1); d.dp++; d=d+Dt(1,1);
      b0[i]=d.dp; b[i]=d.sm; ins(a[i],d);
    }
  Dt d0=qry1(1);
  for(int i=1;i<=n;i++)g[i]=Dt();
  for(int i=n;i;i--)
    {
      Dt d=qry1x(a[i]-1); d.dp++; d=d+Dt(1,1);
      if(b0[i]+d.dp==d0.dp+1)//if!!!
    b[i]=(ll)b[i]*d.sm%mod;
      else b[i]=0;
      insx(a[i],d);
    }
  for(int i=1;i<=n;i++)
    {
      Node d=qry2(a[i]-1); d.dp++;
      p0[i]=d.p0; p1[i]=d.p1;
      s0[i]=d.s0; s1[i]=d.s1;
      d.s0=upt(d.s0+b[i]);d.p0=i;
      if(d.s1)d.s1=upt(d.s1+b[i]),d.p1=i;
      ins(a[i],d);
    }
  Node d1=qry2(n); int sm=d0.sm;
  if(d1.s0==sm)swap(d1.s0,d1.s1),swap(d1.p0,d1.p1);
  if(!d1.s0){puts("-1");return 0;}
  int cr=d1.p0;
  while(cr)
    {
      vis[cr]=1; sm=upt(sm-b[cr]);//sm-=b[cr];
      if(s0[cr]==sm)cr=p1[cr];
      else cr=p0[cr];
    }
  for(int i=1;i<=n;i++)
    if(!vis[i]){int d=qry3(a[i]+1); dp[i]=d+1; ins(a[i],dp[i]);}
  int d=qry3(1),yd=d;
  for(int i=n,w=0;i;i--)
    if(!vis[i]&&a[i]>w&&dp[i]==d){vs[i]=1;w=a[i];d--;}
  printf("%d\n",d1.dp);
  for(int i=1;i<=n;i++)if(vis[i])printf("%d ",i);puts("");
  printf("%d\n",yd);
  for(int i=1;i<=n;i++)if(vs[i])printf("%d ",i);puts("");
  return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Narh/p/10759320.html