近期有价值题目记录3.9~3.15

时间:2019-03-15
本文章向大家介绍近期有价值题目记录3.9~3.15,主要包括近期有价值题目记录3.9~3.15使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1、jzoj5265 缘

题目大意:

给定两棵以1为根的树,i号点在第一棵树中编号为i,在第二棵树中编号为pi,选择一个点对(x,y),要求x!=y,最大化两点在两棵树中lca深度的和,输出和即可。

n<=1e5

题解:

考虑枚举第一棵树中的lca,设为x,问题就变成了求x的不同儿子的子树内点对在第二棵树中lca的最深是多少。

这个可以对儿子的子树一棵一棵地做,看做一个在dfs序上染色的过程,一开始都是白的,每次相当于询问当前点与其他黑点lca的深度最大值,这个最大值必然出现在当前点与前驱或者后继的lca。

这样我们就得到了一个n^2logn的做法,考虑启发式合并,每次先处理轻儿子,重儿子处理完不清空,再合并轻儿子,复杂度变为nlog^2n。

代码比较好写。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int cnt=0,n,p[N],ans;

int tot1=0,he1[N],ne1[N],t1[N];
void link1(int x,int y)
{
    tot1++;
    ne1[tot1]=he1[x];
    he1[x]=tot1;
    t1[tot1]=y;
}

int tot2=0,he2[N],ne2[N],t2[N];
void link2(int x,int y)
{
    tot2++;
    ne2[tot2]=he2[x];
    he2[x]=tot2;
    t2[tot2]=y;
}

int dfn1[N],fr1[N],sz[N],dep1[N],g[N];
void dfs1(int x)
{
    cnt++;
    dfn1[x]=cnt;
    fr1[cnt]=x;
    sz[x]=1;
    for (int i=he1[x];i;i=ne1[i]) 
    {
        dep1[t1[i]]=dep1[x]+1;
        dfs1(t1[i]);
        sz[x]=sz[x]+sz[t1[i]];
        if (sz[t1[i]]>sz[g[x]]) g[x]=t1[i];
    }
}

int dfn2[N],fr2[N],dep2[N],f[N][18];
void dfs2(int x)
{
    cnt++;
    dfn2[x]=cnt;
    fr2[cnt]=x;
    for (int i=he2[x];i;i=ne2[i])
    {
        dep2[t2[i]]=dep2[x]+1;
        f[t2[i]][0]=x;
        for (int j=1;j<=17;j++) f[t2[i]][j]=f[f[t2[i]][j-1]][j-1];
        dfs2(t2[i]);
    }
}

int lca(int x,int y)
{
    if (dep2[x]<=dep2[y]) swap(x,y);
    for (int i=17;i>=0;i--) if (dep2[f[x][i]]>=dep2[y]) x=f[x][i];
    if (x==y) return x;
    for (int i=17;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0]; 
}

int mx[N*4],mn[N*4];
void modify(int rt,int l,int r,int x,int y)
{
    if (l==r)
    {
        if (y==-1) mx[rt]=0,mn[rt]=n+1;
        else mx[rt]=mn[rt]=y;
        return;
    }
    int mid=l+r>>1;
    if (x<=mid) modify(rt<<1,l,mid,x,y);else modify(rt<<1|1,mid+1,r,x,y);
    mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
    mn[rt]=min(mn[rt<<1],mn[rt<<1|1]); 
}

int get_max(int rt,int l,int r,int x,int y)
{
    if (l>=x&&r<=y) return mx[rt];
    int mid=l+r>>1;
    int ret=0;
    if (x<=mid) ret=max(ret,get_max(rt<<1,l,mid,x,y));
    if (y>mid) ret=max(ret,get_max(rt<<1|1,mid+1,r,x,y));
    return ret;
}

int get_min(int rt,int l,int r,int x,int y)
{
    if (l>=x&&r<=y) return mn[rt];
    int mid=l+r>>1;
    int ret=n+1;
    if (x<=mid) ret=min(ret,get_min(rt<<1,l,mid,x,y));
    if (y>mid) ret=min(ret,get_min(rt<<1|1,mid+1,r,x,y));
    return ret;
}

int calc(int x,int y)
{
    if (!x) return -n;
    return dep2[lca(x,y)];
}

void clean(int x)
{
    for (int i=dfn1[x];i<=dfn1[x]+sz[x]-1;i++) modify(1,1,n,dfn2[p[fr1[i]]],-1);
}

void ins(int x)
{
    for (int i=dfn1[x];i<=dfn1[x]+sz[x]-1;i++) 
    {
        int u=get_max(1,1,n,1,dfn2[p[fr1[i]]]),v=get_min(1,1,n,dfn2[p[fr1[i]]],n);
        modify(1,1,n,dfn2[p[fr1[i]]],dfn2[p[fr1[i]]]);
        ans=max(ans,dep1[x]-1+calc(fr2[u],p[fr1[i]]));
        ans=max(ans,dep1[x]-1+calc(fr2[v],p[fr1[i]]));
    }
}

void solve(int x)
{
    for (int i=he1[x];i;i=ne1[i]) if (t1[i]!=g[x]) 
    {
        solve(t1[i]);
        clean(t1[i]);
    }
    if (g[x]) solve(g[x]);
    int u=get_max(1,1,n,1,dfn2[p[x]]),v=get_min(1,1,n,dfn2[p[x]],n);
    modify(1,1,n,dfn2[p[x]],dfn2[p[x]]);
    ans=max(ans,dep1[x]+calc(fr2[u],p[x]));
    ans=max(ans,dep1[x]+calc(fr2[v],p[x]));
    for (int i=he1[x];i;i=ne1[i]) if (t1[i]!=g[x]) ins(t1[i]);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&p[i]);
    for (int i=2;i<=n;i++) 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        link1(x,i);
        link2(y,i);
    }
    dfs1(1);
    cnt=0;
    dfs2(1);
    ans=0;
    for (int i=1;i<=n;i++) modify(1,1,n,i,-1);
    solve(1);
    printf("%d\n",ans);
}

2、newcoder上面的某题

题目大意:

给定n、N,求1~n的N次方的异或和。

n<=1e7,N<=1e9

题解:

考虑线性筛,如果当前数为质数,暴力求幂,否则可以被因子合并出来,复杂度O(n/ln*log)≈O(n)

代码很好写。

#include<bits/stdc++.h>
using namespace std;
const int N=1.3e7+10;
int n,cnt;
int prime[N];
long long f[N];
bool vis[N];
long long Pow(long long a,long long b,long long p)
{
    long long result=1;
    long long base=a%p;
    while(b)
    {
        if(b&1) result=result*base%p;
        base=base*base%p;
        b=b>>1;
    }
    return result;
}
void Euler()
{
    int mo=1e9+7;
    int ans=1;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])
        {
            prime[++cnt]=i;
            f[i]=Pow(i,n,mo);
        }
        ans=ans^f[i];
        for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
        {
            vis[i*prime[j]]=1;
            f[i*prime[j]]=f[i]*f[prime[j]]%mo;
            if(i%prime[j]==0)
            break;
            //f[i*prime[j]]=f[i]*f[prime[j]]%mo;
        }
    }
    printf("%d\n",ans);
}
 
int main()
{
  cin>>n;
  Euler();
  return 0;
}

3、jzoj6053 Mas的仙人掌

题目大意:

给定一棵树和m条边,这m条边有出现的概率,对于所有可能的图的价值为图上新加入的边在至多一个简单环中的个数,求这个价值的期望。

n,m<=1e6

题解:

一条边产生代价,只有在它两端到lca的路径上的连出去的在m条边中的所有边不出现。

考虑打标记,分类讨论...

代码很不好写。

#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
const int N=1e6+10,mo=998244353;
map<long long,int>mp1,mp2;
int bz1[N],bz2[N],tag1[N],tag2[N];
int n,m,tot,he[N];
struct aa
{
    int u,v,x,y,l,p,np;
}q[N];
struct ee
{
    int t,ne;
}e[N*2];
void mul(int &x,int y)
{
    x=(long long)x*y%mo;
}
int ksm(int x,int y)
{
    int res=1;
    while (y)
    {
        if (y&1) mul(res,x);
        mul(x,x);
        y=y>>1;
      }
      return res;
}
void link(int x,int y)
{
    tot++;
    e[tot].ne=he[x];
    he[x]=tot;
    e[tot].t=y;
}
int fa[N],sz[N],son[N],dep[N],las[N];
void dfs1(int x,int y)
{
      sz[x]=1;
      fa[x]=y;
      dep[x]=dep[y]+1;
      for (int i=he[x];i;i=e[i].ne) if (e[i].t!=y)
    {
        int v=e[i].t;
          dfs1(v,x);
        sz[x]=sz[x]+sz[v];
          if (sz[v]>sz[son[x]]) son[x]=v;
    }
}
void dfs2(int x,int y,int z)
{
      las[x]=z;
      if (son[x]) dfs2(son[x],x,z);
      for (int i=he[x];i;i=e[i].ne) if (e[i].t!=y&&e[i].t!=son[x]) dfs2(e[i].t,x,e[i].t);
}
int lca(int x,int y)
{
    while (las[x]!=las[y])
    {
        if (dep[las[x]]<dep[las[y]]) swap(x,y);
        x=fa[las[x]];
    }
      if (dep[x]<dep[y]) return x;
      return y;
}
int get(int x,int y)
{
    if (x==y) return 0;
      while (las[x]!=las[y])
    {
        if (fa[las[x]]==y) return las[x];
        x=fa[las[x]];
      }
      return son[y];
}
long long gethash(long long x,long long y)
{
    if (x>y) swap(x,y);
      return (long long)1000000000*x+y;
}
void modify(aa x)
{
      if (x.np)
    {
        int inv=ksm(x.np,mo-2);
        if (x.x)
        {
              mul(tag1[x.x],x.np);
              mul(tag2[x.u],x.np);
            mul(tag2[x.x],inv);
        }
        if (x.y)
        {
              mul(tag1[x.y],x.np);
              mul(tag2[x.v],x.np);
            mul(tag2[x.y],inv);
        }
        if (x.x&&x.y)
        {
              long long t=gethash(x.x,x.y);
              if (mp1.count(t)) mul(mp1[t],inv);else mp1[t]=inv;
        }
      }else
    {
        if (x.x)
        {
              bz1[x.x]++;
              bz2[x.u]++;
            bz2[x.x]--;
        }
        if (x.y)
        {
              bz1[x.y]++;
              bz2[x.v]++;
            bz2[x.y]--;
        }
        if (x.x&&x.y) --mp2[gethash(x.x,x.y)];
      }
}
int solve(aa x)
{
      int ans=x.p,flag=0;
      if (x.x)
    {
        flag=flag+bz1[x.u]-bz1[x.l]+bz2[x.x];
        mul(ans,tag1[x.u]);
        mul(ans,ksm(tag1[x.l],mo-2));
        mul(ans,tag2[x.x]);
      }
      if (x.y)
    {
        flag=flag+bz1[x.v]-bz1[x.l]+bz2[x.y];
        mul(ans,tag1[x.v]);
        mul(ans,ksm(tag1[x.l],mo-2));
        mul(ans,tag2[x.y]);
      }
      if (x.x&&x.y)
    {
        long long t=gethash(x.x,x.y);
        if (mp1.count(t)) mul(ans,mp1[t]);
        if (mp2.count(t)) flag=flag+mp2[t];
      }
      if (x.np) mul(ans,ksm(x.np,mo-2));else flag--;
      if (flag) return 0;
      return ans;
}
void dfs(int x,int y)
{
      for (int i=he[x];i;i=e[i].ne) if (e[i].t!=y)
    {
        int v=e[i].t;
        mul(tag1[v],tag1[x]);
        bz1[v]=bz1[v]+bz1[x];
          dfs(v,x);
          mul(tag2[x],tag2[v]);
        bz2[x]=bz2[x]+bz2[v];
    }
}
int main() 
{
      scanf("%d%d",&n,&m);
      for (int i=1;i<n;++i) 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        link(x,y);
        link(y,x);
      }
      dfs1(1,0);
    dfs2(1,0,1);
      for (int i=0;i<N;++i) tag1[i]=tag2[i]=1;
      for (int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].p);
        q[i].np=(1-q[i].p+mo)%mo;
        swap(q[i].p,q[i].np);
        q[i].l=lca(q[i].u,q[i].v);
        q[i].x=get(q[i].u,q[i].l);
        q[i].y=get(q[i].v,q[i].l);
        modify(q[i]);
      }
      int sum=0;
      dfs(1,0);
      for (int i=1;i<=m;++i) sum=(sum+solve(q[i]))%mo;
      printf("%d\n",sum);
}

4.jzoj6058 fft

题目大意:

n道题答案为t,m道题答案为f,每次答题会立刻给出答案,以最优策略答题,问期望答错次数。

n、m<=5e5

题解:

数形结合一下,放在坐标系上,发现决策是一条不会超过y=x的折线。

考虑对答案的计算,每当到y=x上时都会产生1/2的贡献,算出每个到(x,x)的方案数即可。

代码很好写呢。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int mo=998244353;
const int N=1e6+10;
long long jc[N],ny[N];
long long ksm(long long x,int y)
{
    x=x%mo;
    long long v=1;
    while (y)
    {
        if (y&1) v=v*x%mo;
        x=x*x%mo;
        y=y>>1;
    }
    return v;
}
long long calc(int x,int y)
{
    return jc[x]*ny[y]%mo*ny[x-y]%mo;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    if (n<m) swap(n,m);
    jc[0]=1;
    for (int i=1;i<=n+m;i++) jc[i]=jc[i-1]*i%mo;
    ny[n+m]=ksm(jc[n+m],mo-2);
    for (int i=n+m-1;i>=1;i--) ny[i]=ny[i+1]*(i+1)%mo;
    ny[0]=1;
    long long ans=0;
    for (int i=1;i<=m;i++) ans=(ans+calc(2*i,i)*calc(n+m-2*i,m-i))%mo;
    ans=((ans*ksm(2*calc(n+m,m),mo-2)+n)%mo+mo)%mo;
    printf("%lld\n",(n+m-ans+mo)%mo);
}

5.某个裸题

题目大意:

求树的拓扑序。

n<=1e5

题解:

结论,有根树的拓扑序等于

n!/∏size(所有子树)

换根即可。

代码很好写。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10,mo=998244353;
int he[N],ne[N*2],t[N*2],tot=0,sz[N*2],n;
long long jc[N],ny[N],ans,mul[N];
void link(int x,int y)
{
    tot++;
    ne[tot]=he[x];
    he[x]=tot;
    t[tot]=y;
}
long long ksm(long long x,int y)
{
    long long ret=1;
    while (y) 
    {
        if (y&1) ret=ret*x%mo;
        y=y>>1;
        x=x*x%mo;
    }
    return ret;
}
void dfs(int x,int y)
{
    sz[x]=1;
    mul[x]=1;
    for (int i=he[x];i;i=ne[i]) if (t[i]!=y)
    {
        dfs(t[i],x);
        sz[x]=sz[x]+sz[t[i]];
        mul[x]=mul[x]*mul[t[i]]%mo;
    }
    mul[x]=mul[x]*ny[sz[x]]%mo*jc[sz[x]-1]%mo;
}
void solve(int x,int y)
{
    ans=(ans+jc[n]*mul[x]%mo)%mo;
    for (int i=he[x];i;i=ne[i]) if (t[i]!=y)
    {
        mul[t[i]]=mul[x]*ny[n-sz[t[i]]]%mo*jc[n-sz[t[i]]-1]%mo*ny[sz[t[i]]-1]%mo*jc[sz[t[i]]]%mo;
        solve(t[i],x);
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++) 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        link(x,y);
        link(y,x);
    }
    jc[0]=1;
    for (int i=1;i<=n;i++) jc[i]=(long long)jc[i-1]*i%mo;
    ny[n]=ksm(jc[n],mo-2);
    for (int i=n-1;i>=0;i--) ny[i]=(long long)ny[i+1]*(i+1)%mo;
    dfs(1,0);
    ans=0;
    solve(1,0);
    printf("%lld\n",ans);
}

6.codeplus6 hard组T1

题目大意:

给出一个序列a[],q组询问l,r,求:

∏(i=l...r-1)∏(j=i+1...r) gcd(a[i],a[j])

n、q、a[i]<=1e5

题解:

分质因子计算答案。

值域比较小,可以考虑分类讨论,分为质因子大于√1e5的和小于等于的。

两部分的答案分别计算,最后乘在一起就好了。

小于等于的部分,答案与每种次数出现的次数有关,O(n√n *log)统计,每次询问是O(√n *log)的。

大于的部分,次数最多为1,考虑分块。

我的分块方法比较猎奇,设块大小为k,对1、1+k、1+2k.....为开头维护区间内的答案。

询问长度小于k的直接暴力,否则找到第一个大于等于l的开头,将前面的暴力。

单次询问复杂度O(√n *log)

暴力的过程我写了一个权值线段树...

总复杂度是O(n√n *log)的。

空间似乎比较也是这么大的,当然优化方法有很多...不累述。

代码想好了之后很好写。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10,mo=998244353;
long long a[N],b[N];
long long mi[66][18];
int vis[N];
long long sz[N];
int c[N][66];
int s[N][66][10],s2[N][20];
int ans[320][N];
long long sum[N];
long long ksm(long long x,int y)
{
    long long ret=1;
    while (y)
    {
        if (y&1) ret=ret*x%mo;
        x=x*x%mo;
        y=y>>1;
    }
    return ret;
}

int g[N],tr[N*20],L[N*20],R[N*20],nd=0;
void ins(int &rt,int l,int r,int x)
{
    if (!rt) 
    {
        nd++;
        rt=nd;
    }
    if (l==r) 
    {
        tr[rt]=1;
        return;
    }
    int mid=l+r>>1;
    if (x<=mid) ins(L[rt],l,mid,x);
    else ins(R[rt],mid+1,r,x);
    tr[rt]=tr[L[rt]]+tr[R[rt]]; 
}

int query(int rt,int l,int r,int x,int y)
{
    if (!rt) return 0;
    if (l>=x&&r<=y) return tr[rt];
    int mid=l+r>>1;
    int ret=0;
    if (x<=mid) ret=ret+query(L[rt],l,mid,x,y);
    if (y>mid) ret=ret+query(R[rt],mid+1,r,x,y);
    return ret;
}

int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    int mx=0,tot=0;
    for (int i=2;i<=100000;i++)
    {
        if (!vis[i])
        {
            tot++;
            sz[tot]=i;
            if (i<=316) mx=tot;
        }
        for (int j=i;j<=100000;j=j+i) vis[j]=1;
    }
    for (int i=1;i<=mx;i++) 
    {
        mi[i][0]=1;
        for (int j=1;j<=17;j++) mi[i][j]=mi[i][j-1]*sz[i]%mo;
    }
    for (int i=1;i<=n;i++) 
    {
        b[i]=a[i];
        for (int j=1;j<=tot;j++) if (sz[j]*sz[j]<=100000)
        {
            while (b[i]%sz[j]==0)
            {
                c[i][j]++;
                b[i]=b[i]/sz[j];
            }
        }else break;
        for (int j=2;j<=mx;j++) 
        {
            for (int k=0;k<=9;k++) s[i][j][k]=s[i-1][j][k];
            s[i][j][c[i][j]]++;
        }
        for (int k=0;k<=17;k++) s2[i][k]=s2[i-1][k];
        s2[i][c[i][1]]++;
        a[i]=a[i]/b[i];
        if (b[i]!=1) ins(g[b[i]],1,n,i);
    }
    int kk=sqrt(n);
    for (int i=1;(i-1)*kk+1<=n;i++)
    {
        for (int j=1;j<=100000;j++) sum[j]=0;
        ans[i][(i-1)*kk]=1;
        for (int j=(i-1)*kk+1;j<=n;j++)
        {
            if (sum[b[j]]) ans[i][j]=(long long)ans[i][j-1]*sum[b[j]]%mo;
            else ans[i][j]=ans[i][j-1];
            if (sum[b[j]]) sum[b[j]]=sum[b[j]]*b[j]%mo;else sum[b[j]]=b[j];
        }
    }
    for (int i=1;i<=q;i++) 
    {
        int l,r;
        scanf("%d%d",&l,&r);
        long long mul=1;
        for (int j=2;j<=mx;j++) 
        {
            long long len=r-l;
            for (int k=0;k<=9;k++) 
            {
                long long tmp=s[r][j][k]-s[l-1][j][k];
                long long gg=(len+len-tmp+1)*tmp/2;
                mul=mul*ksm(mi[j][k],gg)%mo;
                len=len-tmp;
            }
        }
        long long len=r-l;
        for (int k=0;k<=17;k++)
        {
            long long tmp=s2[r][k]-s2[l-1][k];
            long long gg=(len+len-tmp+1)*tmp/2;
            mul=mul*ksm(mi[1][k],gg)%mo;
            len=len-tmp;
        }
        if (r-l+1<=kk) 
        {
            for (int j=l;j<=r;j++) if (b[j]!=1) 
            {
                int tmp=query(g[b[j]],1,n,j+1,r);
                mul=mul*ksm(b[j],tmp)%mo;
            }
        }else
        {
            int j=1;
            while ((j-1)*kk+