第一题是 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; }