省队集训DAY5

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/clover_hxy/article/details/72859662

T1

这里写图片描述

题解

因为是本质不同的字符串,所以考虑什么样的串会产生贡献。
对于一个字符串,我们使他出现的位置尽可能的考前,就是如果这个位置能从第i个串中匹配一定不会在第i+1个串中匹配。这样最先拼凑出的字符串产生贡献1。
如果一个串我们要求本质不同的所有子串,该怎么做?对于原串建立后缀自动机,因为根节点到后缀自动机中的每个节点形成的路径都是一个本质不同的子串,所以我们按照拓扑须的倒叙更新大, ans[x]=(26i=1ans[ch[x][i]])+1 .
现在有多个串,如何连接呢?从后向前对于每个串依次建立后缀自动机。对于后缀自动机中的每个节点枚举字符集,如果第i个儿子为空,就是没有字符为i的后继,那么我们就把他的指针直到离他最近的与他不在同一个串且字符为i的节点上。
每处理完一个串,就用这个串根节点的每个儿子更新mark数组。
mark[i]表示字符i最近的出现位置。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 2020003
#define mod 1000000007
using namespace std;
int ans[N],ch[N][30],fa[N],l[N],v[N];
int last,p,q,np,nq,cnt,n,pos[N],mark[N],sum,tot;
char s1[N],s[N];
struct data{
    int len,L,R;
    int root;
    void init(){
        scanf("%s",s1+1);
        len=strlen(s1+1); L=tot+1;
        for(int i=1;i<=len;i++) s[++tot]=s1[i];
        R=tot;
    }
    void insert(int c)
    {
        p=last; np=++cnt; l[np]=l[p]+1;
        last=np;
        for (;!ch[p][c]&&p;p=fa[p]) ch[p][c]=np;
        if (!p) fa[np]=root;
        else {
            q=ch[p][c];
            if (l[p]+1==l[q]) fa[np]=q;
            else {
                nq=++cnt; l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[nq]));
                fa[nq]=fa[q];
                fa[np]=fa[q]=nq;
                for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
            }
        }
    }
    void build(){
        root=last=++cnt; int head=cnt; 
        for (int i=L;i<=R;i++) insert(s[i]-'a'+1);
        for (int i=0;i<=len;i++) v[i]=0;
        for (int i=head;i<=cnt;i++) v[l[i]]++;
        for (int i=1;i<=len;i++) v[i]+=v[i-1];
        int t=0;
        for (int i=cnt;i>=head;i--) pos[v[l[i]]--]=i,t++;
        for (int i=t;i>=1;i--) {
            int now=pos[i];
            for (int j=1;j<=26;j++) {
                int x=ch[now][j];
                if (!x) x=mark[j];
                ans[now]=(ans[now]+ans[x])%mod;
            }
            ans[now]=(ans[now]+1)%mod;
        }
        for (int i=1;i<=26;i++)
         if (ch[root][i])mark[i]=ch[root][i];
    }
}str[N];
int main()
{
    freopen("str.in","r",stdin);
    freopen("str.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) str[i].init();
    for (int i=n;i>=1;i--) str[i].build();
    printf("%d\n",ans[str[1].root]);    
}

T2

这里写图片描述
这里写图片描述

题解

将权值为-1的点看成是白点,那么问题就变成了去掉的点权值>=sum-limit的方案数。
设s表示选中的集合。
f(s) 表示只有集合中的点只连白点的生成树的个数。
g(s) 表示强制集合中的点只连白点的生成树的个数。对于 g(s) 让s中的点在矩阵中只连白点,直接用基尔霍夫矩阵求生成树的个数即可。因为不能保证不在集合中的点周围不全是白点,所以f(s)需要利用容斥来求解。
可以发现f,g的值其实只与|s|有关,那么我们不妨设 f(size),g(size) 分别表示集合中选中size个点的方案数。
f(size)=ni=size(1)isizef(i)
然后考虑如何求出集合中的元素个数为x,权值之和>=sum-limit的个数 h(x)
把序列分成两部分,对于两部分分别做指数级别爆搜,然后将得到的所有方案按照点权和排序,再考虑两部分合起来的方案,第一部分从前向后枚举,第二部分指针从后向前开始扫。统计答案即可
ans=ni=0h(i)f(i)
时间复杂度 O(2n(n/2)+n4)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 103
#define p 1000000007
#define LL long long 
using namespace std;
LL f[N],g[N],h[N],C[N][N],a[N][N];
int n,limit,st[N],top,st1[N],top1,num[N],pd[N],pd1[N];
struct node{ int x,y; };
struct data{
    int a[N]; node v[2000003];
    int cnt,n;
    void dfs(int x,int sum,int dep) {
        if (x==n+1) {
            ++cnt; v[cnt].x=sum; v[cnt].y=dep;
            return;
        }
        dfs(x+1,sum,dep);
        dfs(x+1,sum+a[x],dep+1);
    }
}l2,l1;
int cmp(node a,node b){
    return a.x<b.x||a.x==b.x&&a.y<b.y;
}
int cmp1(node a,node b){
    return a.x>b.x||a.x==b.x&&a.y<b.y;
}
int quickpow(LL num,int x)
{
    LL base=num%p; LL ans=1;
    while (x) {
        if (x&1) ans=ans*base%p;
        x>>=1;
        base=base*base%p;
    }
    return ans;
}
LL guass(int n)
{
    LL ret=1; 
    for (int i=1;i<=n;i++) {
        int num=i;
        for (int j=i+1;j<=n;j++)
         if (abs(a[j][i])>abs(a[num][i])) num=j;
        if (num!=i) {
            ret*=-1;
            for (int j=1;j<=n;j++) swap(a[i][j],a[num][j]);
        }
        for (int j=i+1;j<=n;j++) 
         if (a[j][i]){
            LL t=a[j][i]*quickpow(a[i][i],p-2)%p;
            for (int k=1;k<=n;k++)
             a[j][k]=a[j][k]-a[i][k]*t,a[j][k]%=p;
        }
        ret=ret*a[i][i]%p;
    }
    return (ret%p+p)%p;
}
int main()
{
    freopen("apple.in","r",stdin);
    freopen("apple.out","w",stdout);
    scanf("%d%d",&n,&limit);
    int sum=0;
    for (int i=0;i<=n;i++) C[i][0]=1;
    for (int i=1;i<=n;i++)
     for (int j=1;j<=i;j++)
      C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
    for (int i=1;i<=n/2;i++) {
        int x; scanf("%d",&x); 
        if (x!=-1) 
         l1.n++,l1.a[l1.n]=x,st[++top]=i,sum+=x;
        else st1[++top1]=i,pd1[i]=1;
    }
    for (int i=n/2+1;i<=n;i++) {
        int x; scanf("%d",&x); 
        if (x!=-1)
         l2.n++,l2.a[l2.n]=x,st[++top]=i,sum+=x;
        else st1[++top1]=i,pd1[i]=1;
    }
    l1.dfs(1,0,0);
    l2.dfs(1,0,0);
    sort(l1.v+1,l1.v+l1.cnt+1,cmp);
    sort(l2.v+1,l2.v+l2.cnt+1,cmp);
    limit=sum-limit;
    int pos=l2.cnt;
    for (int i=1;i<=l1.cnt;i++){
        while (l1.v[i].x+l2.v[pos].x>=limit&&pos>0) {
            num[l2.v[pos].y]++;
            pos--;
        }
        for (int j=0;j<=n;j++) h[j+l1.v[i].y]+=num[j],h[j+l1.v[i].y]%=p;
    }
    for(int i=0;i<=top;i++){
        memset(a,0,sizeof(a));
        memset(pd,0,sizeof(pd));
        for (int j=1;j<=i;j++) pd[st[j]]=1;
        for (int j=1;j<=n;j++) 
         for (int k=j+1;k<=n;k++) {
            if (j==k) continue;
            if (pd[j]) {
                if (!pd1[k]) continue;
                a[j][k]--; a[k][j]--;
                a[j][j]++; a[k][k]++;
             }
            else {
                a[j][k]--; a[k][j]--;
                a[j][j]++; a[k][k]++;
            }
         }
        g[i]=guass(n-1);
    }
    for (int i=0;i<=n;i++) {
     for (int j=i;j<=n;j++) {
      LL t=C[n-top1-i][j-i]; 
      if ((j-i)&1) t*=-1;
      f[i]=(f[i]+t*g[j]%p)%p;
     }
     f[i]=(f[i]%p+p)%p;
    }
    LL ans=0;
    for (int i=0;i<=n;i++) ans=(ans+f[i]*h[i]%p)%p;
    printf("%I64d\n",ans);
}

T3

这里写图片描述
这里写图片描述

题解

这道题可以说是一上来毫无头绪啊,知道正解后发现处理的十分巧妙,是一道不错的题。
设ans[x]表示x到root路径上所有点代表的p[i]到k的答案和。
那么 ilrdis(p[i],k)=ans[l]+ans[r]ans[lca(x,y)]ans[fa[lca(x,y)]]
那么问题的关键就是怎么求ans[x]
ans[x]=(ixrootdistoroot[p[i]])+deep[x]distoroot[k]2ixrootdistoroot[lca(p[i],k)]
预处理dissum[x]表示 ixrootdistoroot[p[i]] ,那么前两项可以直接计算。
关键就是怎么求第三项。把边权下放成点权,我们把x到root所有的p[i]到根路径上经过的点在线段树中打上标记(线段树中某些区间的权值和整体+1),那么如果我们要查询lca实际上就是查询k到根路径上的点在线段树中的权值和。
对于每个点来说,只是在他父亲的基础上加入了一条新的路径,可以我们可以用主席树的方式来存储信息。继承父亲的信息,只有有更新的位置重新开点。
因为牵扯到区间修改,如果标记下放的话会产生很多没有用的节点。所以我们考虑标记永久化,对于每个区间维护add表示区间整体被覆盖的次数,sum表示区间的答案。
sum[i]=sum[now]add+sum[l]+sum[r] 其中sum’表示的是区间和。
因为不会进行区间下放,所以查询的时候要统计路径上的add标记,最后一个区间的答案是 sum+sumaddsum[now]

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 400003
#define LL long long 
using namespace std;
struct data{
    int ls,rs; LL sum,add;
}tr[N*80];
int n,m,mi[20],f[N][20],fa[N],tot,nxt[N],v[N],point[N],size[N],belong[N],son[N],pos[N],q[N],cnt,sz,root[N],p[N],L[N];
LL type,c[N],deep[N],distoroot[N],sum[N*4],val[N],dissum[N];
void add(int x,int y,int z)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void dfs(int x,int father)
{
    deep[x]=deep[father]+1; size[x]=1;
    for (int i=1;i<=18;i++) {
        if (deep[x]-mi[i]<0) break;
        f[x][i]=f[f[x][i-1]][i-1];
    }
    for (int i=point[x];i;i=nxt[i]){
        if (v[i]==father) continue;
        f[v[i]][0]=x; fa[v[i]]=x;
        distoroot[v[i]]=distoroot[x]+c[i];
        val[v[i]]=c[i];
        dfs(v[i],x);
        size[x]+=size[v[i]];
        if (size[v[i]]>size[son[x]]) son[x]=v[i];
    }
}
void dfs1(int x,int chain)
{
    belong[x]=chain; pos[x]=++cnt; q[cnt]=x;
    if(!son[x]) return;
    dfs1(son[x],chain);
    for (int i=point[x];i;i=nxt[i])
     if (v[i]!=son[x]&&v[i]!=fa[x])
      dfs1(v[i],v[i]);
}
void build(int now,int l,int r)
{
    if (l==r) {
        sum[now]=val[q[l]];
        return;
    }
    int mid=(l+r)/2;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    sum[now]=sum[now<<1]+sum[now<<1|1];
}
int lca(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int k=deep[x]-deep[y];
    for (int i=0;i<=18;i++)
     if ((k>>i)&1) x=f[x][i];
    if (x==y) return x;
    for (int i=18;i>=0;i--)
     if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
void update(int now,int x)
{
    int l=tr[x].ls; int r=tr[x].rs;
    tr[x].sum=sum[now]*tr[x].add;
    if (l) tr[x].sum+=tr[l].sum;
    if (r) tr[x].sum+=tr[r].sum;
}
void insert(int &i,int j,int now,int l,int r,int ll,int rr,int t)
{
    if (i<=t) i=++sz,tr[i]=tr[j];
    if (ll<=l&&r<=rr) {
        tr[i].add++;
        update(now,i);
        return;
    }
    int mid=(l+r)/2;
    if (ll<=mid) insert(tr[i].ls,tr[j].ls,now<<1,l,mid,ll,rr,t);
    if (rr>mid) insert(tr[i].rs,tr[j].rs,now<<1|1,mid+1,r,ll,rr,t);
    update(now,i);
}
LL query(int i,int now,int l,int r,int ll,int rr,LL v)
{
    if (ll<=l&&r<=rr) return tr[i].sum+sum[now]*v;
    int mid=(l+r)/2;
    LL ans=0;
    if (ll<=mid) ans+=query(tr[i].ls,now<<1,l,mid,ll,rr,v+tr[i].add);
    if (rr>mid) ans+=query(tr[i].rs,now<<1|1,mid+1,r,ll,rr,v+tr[i].add);
    return ans; 
}
void add(int x,int father)
{
    int t=x;
    x=p[x]; L[t]=sz;
    while (x){
        insert(root[t],root[father],1,1,n,pos[belong[x]],pos[x],L[t]);
        x=fa[belong[x]];
    }
}
void solve(int x,int father)
{
    dissum[x]=dissum[father]+distoroot[p[x]];
    add(x,father);
    for (int i=point[x];i;i=nxt[i]){
        if (v[i]==father) continue;
        solve(v[i],x);
    }
}
LL calc(int x,int k)
{
    int t=root[x];
    LL ans=0;
    while (k) {
        ans+=query(t,1,1,n,pos[belong[k]],pos[k],0);
        k=fa[belong[k]];
    }
    return ans;
}
LL getans(int x,int k)
{
    return dissum[x]+distoroot[k]*deep[x]-(LL)2*calc(x,k);
}
int main()
{
    freopen("dis.in","r",stdin);
    freopen("dis.out","w",stdout);
    scanf("%I64d",&type); mi[0]=1;
    for (int i=1;i<=19;i++) mi[i]=mi[i-1]*2;
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++) {
        int x,y,z; scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    for (int i=1;i<=n;i++) scanf("%d",&p[i]);
    dfs(1,0); dfs1(1,1);
    build(1,1,n);
    solve(1,0);
    LL ans=0;
    for (int i=1;i<=m;i++) {
        LL x,y,k; scanf("%I64d%I64d%I64d",&x,&y,&k);
        x=x^(type*ans); y=y^(type*ans); k=k^(type*ans);
        int t=lca(x,y); 
        ans=getans(x,k)+getans(y,k)-getans(t,k)-getans(fa[t],k);
        printf("%I64d\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/clover_hxy/article/details/72859662
今日推荐