UOJ Logo snakes的博客

博客

[Tjoi2016&Heoi2016]题解

2016-08-18 21:31:40 By snakes

[Tjoi2016&Heoi2016]题解

By Snakes

(好不容易做完一套题……)

A Tree

离线处理,将所有操作倒序,并用并查集维护。对于标记操作,只需要在每个结点倒序之后,最后被执行标记操作的时刻与它父亲合并即可。对于询问操作,只需要查询该结点所在并查集的父亲。可以添加路径压缩优化。时间复杂度$O(n+q\alpha(n))$。

还可以用树链剖分哟~(如果你是树剖爱好者QAQ)

B Sort

30分考验你对C++的sort的使用熟练程度。(像我这种一开始打了30分暴力还在自定义cmp的时候把升序降序搞反也是没救了)

考虑二分答案,若当前答案为mid,那我们处理出一个长度为n的01序列,如果原序列第i位比mid小,那么标记为0,否则标记为1。新序列可以用线段树维护,排序过程相当于查询一次区间和以及两次区间修改。升序排序就将区间中的1移到右边,降序排序相当于将区间中的1移到左边。如果所有排序结束后,第q位为1,那么就表示该位置大于或等于mid,否则小于mid。继续二分即可。二分时间复杂度$O(logn)$,每次排序时间复杂度$O(mlogn)$,那么总的时间复杂度为$O(mlog^2n)$

C Seq

先考虑dp,令$f(i)$为第i个位置结尾的最优答案,不考虑某些项的值发生变化的时候可以用$f(i)=max(f(j))+1|j < i,a[j]<=a[i]$转移。记录一个位置能变化的最小值$b[i]$,最大值$c[i]$,那么考虑值发生变化的影响就可以用$$f(i)=max(f(j))+1|j < i,a[j]<=a[i],c[j]<=a[i],a[j]<=b[i]$$转移啦。如果将i从小到大扫描,每次扫描完一个点,将i加入数据结构中,那么就可以用kd-tree或者是cdq分治做啦。我用四分树也能做,直接维护最大值即可。查询的时候可以直接在树上查询满足$a[j]<=min(a[i],b[i]),c[j]<=a[i]$的最大值。时间复杂度$O(nlog^2n)$。

代码如下~

#include <cstdio>
#include <algorithm>
#define maxn 1<<19
#define maxt 100005
using namespace std;
struct node
{
    int ch[4],data;
};
node tree[maxn];
int root,glob_tot;
inline int new_node(int val)
{
    tree[++glob_tot].data=val;
    return glob_tot;
}
int x,y,val;
inline void insert(int lx,int rx,int ly,int ry,int &p)
{
    if (p==0)
        p=new_node(val);
    else
        tree[p].data=max(tree[p].data,val);
    if (lx^rx)
    {
        int mx=(lx+rx)>>1,my=(ly+ry)>>1;
        if (x>mx)
            if (y>my)
                insert(mx+1,rx,my+1,ry,tree[p].ch[0]);
            else
                insert(mx+1,rx,ly,my,tree[p].ch[1]);
        else
            if (y>my)
                insert(lx,mx,my+1,ry,tree[p].ch[3]);
            else
                insert(lx,mx,ly,my,tree[p].ch[2]);
    }
}
inline void query(int lx,int rx,int ly,int ry,int p)
{
    if (p==0||tree[p].data<val)
        return;
    if (rx<=x&&ry<=y)
    {
        val=tree[p].data;
        return;
    }
    int mx=(lx+rx)>>1,my=(ly+ry)>>1;
    if (x>mx)
        if (y>my)
        {
            query(mx+1,rx,my+1,ry,tree[p].ch[0]);
            query(mx+1,rx,ly,my,tree[p].ch[1]);
            query(lx,mx,ly,my,tree[p].ch[2]);
            query(lx,mx,my+1,ry,tree[p].ch[3]);
        }
        else
        {
            query(mx+1,rx,ly,my,tree[p].ch[1]);
            query(lx,mx,ly,my,tree[p].ch[2]);
        }
    else
        if (y>my)
        {
            query(lx,mx,ly,my,tree[p].ch[2]);
            query(lx,mx,my+1,ry,tree[p].ch[3]);
        }
        else
            query(lx,mx,ly,my,tree[p].ch[2]);
}
int n,m,a[maxt],b[maxt],c[maxt],i,j,ans;
int main()
{
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=c[i]=a[i];
    }
    for (i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        if (y<b[x])
            b[x]=y;
        if (y>c[x])
            c[x]=y;
    }
    for (i=1;i<=n;i++)
    {
        x=a[i];
        y=b[i];
        val=0;
        query(1,maxn,1,maxn,root);
        val++;
        if (val>ans)
            ans=val;
        x=c[i];
        y=a[i];
        insert(1,maxn,1,maxn,root);
    }
    printf("%d\n",ans);
    return 0;
}

D Game

二分图匹配的裸题。对于每个硬石头,将它左右和上下分别看成不同的行与不同的列。对于每个空地,将与它一行的标号和与它一列的标号连边,行与列作为二分图的两边,求最大匹配即可。可以证明这样连边满足每个行联通的块与列联通的块最多只会放一个炸弹。时间复杂度$O(VE)$,其中V为点数,E为边数。

E Sum

数论变态题QAQ

套第二类斯特林数展开式可以得到

$$ \begin{align} f(n)&=\sum_{i=0}^n\sum_{j=0}^i S(i,j)*2^j*(j!)\\ &=\sum_{i=0}^n\sum_{j=0}^n S(i,j)*2^j*(j!)\\ &=\sum_{i=0}^n\sum_{j=0}^n 2^j*(j!)* \frac 1{j!}*\sum_{k=0}^j(-1)^kC_k^j(j-k)^i\\ &=\sum_{i=0}^n\sum_{j=0}^n 2^j*\sum_{k=0}^j(-1)^{j-k}C_k^jk^i\\ &=\sum_{i=0}^n\sum_{j=0}^n 2^j*\sum_{k=0}^j(-1)^{j-k}*\frac{j!}{k!(j-k)!}*k^i\\ &=\sum_{j=0}^n 2^j*\sum_{k=0}^j(-1)^{j-k}*\frac{j!}{k!(j-k)!}*\sum_{i=0}^nk^i\\ &=\sum_{j=0}^n 2^j*j!*\sum_{k=0}^j\frac{(-1)^{j-k}}{(j-k)!}*\frac{\sum_{i=0}^nk^i}{k!} \end{align} $$

其中$\sum_{k=0}^j\frac{(-1)^{j-k}}{(j-k)!}*\frac{\sum_{i=0}^nk^i}{k!}$交给我们伟大的NTT处理,令$a_i=\frac{(-1)^{j-i}}{(j-i)!}$,$b_i=\frac{\sum_{i=0}^nk^i}{k!}=\frac{i^{n+1}-1}{(i-1)*i!}$,$c_i=\sum_{k=0}^ia_k*b_{i-k}$,则$ans=\sum_{j=0}^n 2^j*j!*c_j$。

代码见下~(另附一超级猎奇版本,见文末。)

#include <cstdio>
using namespace std;
#define maxn 1<<19
#define mod 998244353
#define g 3
long long n,i,dft_len,pow2,fac;
long long fac_inv[maxn];
long long a[maxn],b[maxn],c[maxn];
long long rev[maxn],w[maxn],v[maxn];
long long ans;
long long q_pow(long long x,long long p)
{
    long long ans=1;
    while (p)
    {
        if (p&1)
            ans=ans*x%mod;
        x=x*x%mod;
        p>>=1;
    }
    return ans;
}
void init()
{
    long long i,j,t;
    for (i=0;i<dft_len;i++)
        for (j=1,t=i;j<dft_len;j<<=1,t>>=1)
            rev[i]<<=1,rev[i]+=t&1;
    w[0]=1;
    w[1]=q_pow(g,(mod-1)/dft_len);
    for (i=2;i<=dft_len;i++)
        w[i]=w[i-1]*w[1]%mod;
}
void dft(long long a[],long long p)
{
    long long i,j,k,tot=dft_len,c,x,y;
    for (i=0;i<dft_len;i++)
        v[rev[i]]=a[i];
    for (i=2;i<=dft_len;i<<=1)
    {
        tot>>=1;
        c=p?dft_len:0;
        for (j=0;j<i>>1;j++)
        {
            for (k=j;k<dft_len;k+=i)
            {
                x=v[k];
                y=v[k+(i>>1)]*w[c]%mod;
                v[k]=(x+y)%mod;
                v[k+(i>>1)]=(x-y+mod)%mod;
            }
            if (p)
                c-=tot;
            else
                c+=tot;
        }
    }
    for (i=0;i<dft_len;i++)
        a[i]=v[i];
}
void ntt()
{
    long long inv=q_pow(dft_len,mod-2);
    init();
    dft(a,0);
    dft(b,0);
    for (i=0;i<dft_len;i++)
        c[i]=a[i]*b[i]%mod;
    dft(c,1);
    for (i=0;i<dft_len;i++)
        c[i]=c[i]*inv%mod;
}
int main()
{
    scanf("%d",&n);
    dft_len=1;
    while (dft_len<=n<<1)
        dft_len<<=1;
    fac_inv[0]=1;
    for (i=1;i<=n;i++)
        fac_inv[i]=(fac_inv[i-1]*i)%mod;
    for (i=0;i<=n;i++)
        fac_inv[i]=q_pow(fac_inv[i],mod-2);
    for (i=0;i<=n;i++)
        a[i]=i&1?(-fac_inv[i]+mod)%mod:fac_inv[i];
    b[0]=1;
    b[1]=n+1;
    for (i=2;i<=n;i++)
        b[i]=(q_pow(i,n+1)-1)*q_pow(i-1,mod-2)%mod*fac_inv[i]%mod;
    ntt();
    pow2=1;
    fac=1;
    for (i=0;i<=n;i++)
    {
        ans=(ans+pow2*fac%mod*c[i]%mod)%mod;
        pow2=(pow2<<1)%mod;
        fac=fac*(i+1)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

F Str

用SA求出height数组,暴力二分答案并查询,由于bzoj时限20s,所以最坏时间复杂度$O(nlogn+mnlogn)$刚好过了……(论玄学的威力QAQ)

正解需要离线处理,按照询问的a降序排序。线段树维护,二分查询。复杂度$O(mlog^2n)$。

~~~完结撒花~~~

谁说完结了?

E Sum

超级猎奇版本(缩小屏幕后食用更加)

#include                    <cstdio>
const long                 long N=1
<<19;  const               long long
mod=998244353;             long long
g=3;long long n,           i,dft_len
,pow2,j,  fac,ans;         long long
fac_inv[   N];long         long a[N]
,b[N],c     [N];long      long rev[N]
,w[N],v      [N];long      long q_pow
(  long        long x,     long long
p){long         long ans   =1;while(
p){if(p&         1) ans=  ans*x%mod;
x=x*x%            mod;p>>= 1;}return
ans;  }             void init(){long
long i,               t;for(i=0*0;i<
dft_len;               i++)for(j=2-1
,t=i;j<                 dft_len;j<<=
1,t>>=1)                 rev[i]<<=1,
rev[i]+=                  t&1;w[0]=1;


w[1]=q_pow(g,(mod-1)/dft_len);for (i=
2;i<=dft_len;i++)w[i]=w[i-1]*w[1]%mod;
}void dft(long long a[],long long p){
             long long
             i,j,k,tot
             =dft_len,
             c,x,y;for
             ( i=0; i<
             dft_len;i
             ++)v[rev[
             i]]=a[i];
             for(i=2;i
             <=dft_len
             ;i<<=2-1)
             {tot>>=1;
             c=p|p|p|p
             ?dft_len:
             0;for(j=0
             ;j<i>>1;j
             ++){for(k


=j;k<dft_len;k+=i){x=v[k];y=v[k+(i>>1)
]*w[c]%mod;v[k]=(x+y)%mod;v[k+(i>>1)]=
(x-y+mod)%mod;}if(p)c-=tot;else c+=tot;
             }}for(i=0;
             i<dft_len
             ;i++)a[i]
             =v[i]*1;}
             void ntt(
             ){/*ntt*/
             long long
             inv=q_pow
             (dft_len,
             mod-1-1);
             init();dft
             (a,0);dft
             (b,0);for
             (i=1-1;i<
             dft_len;i
             ++) c[i]=


           a[i]*b[i]%mod;
           dft(c,1);for(
           i=0;i<dft_len
           ;i++)c[i]=c[i
           ]*inv%mod+1-1
           ;}int main(){
           scanf("%d",&n
           );dft_len=1;;
           while(dft_len
           <=n*2)dft_len
           <<=1;fac_inv[
           0]=1;for(i=1;
           i<=n;i++,i=i)
           fac_inv[i]= (
           fac_inv[i-1]*
           i)%mod;for(i=
           1-1;i<=n;i++)
           fac_inv[i]=1*
           q_pow(fac_inv
            [i],mod-2);
            for(i=0;i<=
            n;i++)a[i]=
             i&1?(-1*1
             *fac_inv[
             i]+mod)%

           mod:fac_inv[i
           ];b[0]=1;b[1]
           =n+1;for(i=2;
           i<=n;i++)b[i]
           =(q_pow(i,n+1
           )-1)*q_pow(i-


  1,mod-2                                )%
mod                                      *
 fac_inv[                               i]
       %mod   ;; ntt();    pow2=1;      fac=1;       for(i=0      ;i<=n
        ;i++   ){    ans        =1-    1+(   ans   +pow2  *fac   %mod
        *c[i  ]%     mod    )%mod;     pow2=(pow2  <<1)%mod;;      fac=
       fac    *(i    +1)  %mod  ;}    i=i   ;i;    fac;             n++;
printf(       /*:    )*/   "%lld\n",  ans     );;    return 0;  n=n-i;}

~~~~真正的完结撒花~~~~

评论

dram
snakes 泥嚎啊
ridiculos
snakes泥嚎啊
Drench
考试的时候d1t2 sort暴力有80分
shanquan2
sum不是oeis题吗?(逃)

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。