6.26联考题解

A:
首先答案的下界是 l 1 + l c
对长为 l ,字符集为 c 的所有串建进一个图里,每个串连 c 条边分别连向添加这个字符后这个串长为 l 的后缀的串的点,感受一下这个图显然有哈密顿回路且这个哈密顿回路就是我们要找的最优解
然鹅找哈密顿回路是NP的,考虑把他转化成找欧拉回路,用边代表这个图中的点
我们建一个新图,把这些串的所有长为 l 1 的前缀作为点,连 c 条边,每条边连向添加这个字符后该串长为 l 1 的后缀代表的点,问题就变成了找一条欧拉回路,就可以直接做了

空间卡的很紧,要用一些技巧比如边不用建出来什么的卡一下空间….
好像还要手写栈….

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 12000000;

int C,L;
char str[110];
int nowi[maxn],sum;

int sta[maxn],top;
char temp[maxn],ans[maxn]; int ansn;
void solve(int x)
{
    sta[top=1]=x;
    while(top)
    {
        if(temp[top]!=0) ans[++ansn]=temp[top],temp[top]=0;
        x=sta[top];
        if(nowi[x]<C)
        {
            int y=(x*C+nowi[x])%sum;
            temp[top]=str[++nowi[x]];
            sta[++top]=y;
        }
        else top--;
    }
}

int main()
{
    freopen("life.in","r",stdin);
    freopen("life.out","w",stdout);

    scanf("%d%d",&C,&L);
    scanf("%s",str+1);
    sum=1;for(int i=1;i<L;i++) sum*=C;

    int x=0;
    for(int i=1;i<L;i++) x=x*C;
    solve(x);

    printf("%d\n",L-1+ansn);
    for(int i=1;i<L;i++) putchar(str[1]);
    for(int i=ansn;i>=1;i--) putchar(ans[i]);
    puts("");

    return 0;
}

B:
在原串字符间插分隔符方便处理偶数长度回文串
跑Manacher,对每个位置求出最长的回文半径rad[i]
考虑怎么计算 [ l , r ] 内的回文串个数
答案其实就是

i = l r m i n ( m i n ( i l + 1 , r + 1 i ) , r a d [ i ] ) 2

分类讨论把min拆了,然后变成求二维平面矩形内的点权和,用可持久化线段树就好了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 310000;
const int maxp = maxn*30;

struct segment
{
    int cnt;
    int lc[maxp],rc[maxp],siz[maxp];
    ll seg[maxp],seg2[maxp];
    int loc,c1,c2;
    void upd(int &x,const int l,const int r)
    {
        x=++cnt;
        seg[x]=c1,seg2[x]=c2,siz[x]=1;
        if(l==r) return;
        int mid=(l+r)>>1;
        loc<=mid?upd(lc[x],l,mid):upd(rc[x],mid+1,r);
    }
    void merge(int &x,const int &y)
    {
        if(!y) return;
        if(!x){ x=y;return; }
        siz[x]+=siz[y];
        seg[x]+=seg[y],seg2[x]+=seg2[y];
        merge(lc[x],lc[y]); merge(rc[x],rc[y]);
    }
    int lx,rx;
    ll querysum1(int x,int y,const int l,const int r)
    {
        if(rx<l||r<lx||siz[x]==siz[y]) return 0;
        if(lx<=l&&r<=rx) return seg[x]-seg[y];
        int mid=(l+r)>>1;
        return querysum1(lc[x],lc[y],l,mid)+querysum1(rc[x],rc[y],mid+1,r);
    }
    ll querysum2(int x,int y,const int l,const int r)
    {
        if(rx<l||r<lx||siz[x]==siz[y]) return 0;
        if(lx<=l&&r<=rx) return seg2[x]-seg2[y];
        int mid=(l+r)>>1;
        return querysum2(lc[x],lc[y],l,mid)+querysum2(rc[x],rc[y],mid+1,r);
    }
    int querysiz(int x,int y,const int l,const int r)
    {
        if(rx<l||r<lx||siz[x]==siz[y]) return 0;
        if(lx<=l&&r<=rx) return siz[x]-siz[y];
        int mid=(l+r)>>1;
        return querysiz(lc[x],lc[y],l,mid)+querysiz(rc[x],rc[y],mid+1,r);
    }
}seg,seg2;
int root[maxn],root2[maxn];

int n,m,len;
int S[maxn],rad[maxn];
char str[maxn];

int main()
{
    freopen("gene.in","r",stdin);
    freopen("gene.out","w",stdout);

    scanf("%d%d",&n,&m);
    scanf("%s",str+1); len=n;
    S[n=0]=-1;
    for(int i=1;i<=len;i++)
    {
        S[++n]=0;
        S[++n]=str[i]-'A'+1;        
    }
    S[++n]=0;
    S[++n]=-2;

    int mx=0,id=0;
    for(int i=1;i<n;i++)
    {
        if(mx>i) rad[i]=min(mx-i,rad[2*id-i]);
        while(S[i-rad[i]]==S[i+rad[i]]) rad[i]++;

        if(i+rad[i]>mx)
            id=i,mx=i+rad[i];
    }

    for(int i=1;i<n;i++)
    {
        seg.loc=i-rad[i],seg.c1=rad[i]>>1,seg.c2=i>>1,seg.upd(root[i],0,n);
        seg.merge(root[i],root[i-1]);

        seg2.loc=min(i+rad[i],n),seg2.c1=rad[i]>>1,seg2.c2=(i+1)>>1,seg2.upd(root2[i],0,n);
        seg2.merge(root2[i],root2[i-1]);
    }

    while(m--)
    {
        int l,r; scanf("%d%d",&l,&r);
        l=2*l-1,r=2*r+1; int mid=(l+r)>>1;
        ll ans=0;

        seg.lx=l,seg.rx=n,ans+=seg.querysum1(root[mid],root[l-1],0,n);

        seg.lx=0,seg.rx=l-1;
        ll si=seg.querysum2(root[mid],root[l-1],0,n);
        int siz=seg.querysiz(root[mid],root[l-1],0,n);
        ans+=si-(ll)siz*((l-1)/2);

        seg2.lx=0,seg2.rx=r,ans+=seg2.querysum1(root2[r],root2[mid],0,n);

        seg2.lx=r+1,seg2.rx=n;
        si=seg2.querysum2(root2[r],root2[mid],0,n);
        siz=seg2.querysiz(root2[r],root2[mid],0,n);
        ans+=(ll)siz*((r+1)/2)-si;

        printf("%lld\n",ans);
    }

    return 0;
}

C:
对每个位置二分求出能覆盖他的点的范围 [ l , r ]
问题相当于你可以设置不超过 m 个关键点,在位置 i 设置一个关键点有花费 C i ,有若干条线段 ( x , y ) 表示若 [ x , y ] 内没有关键点就要额外支付 P i 的花费,求最小花费
f [ i ] [ j ] 表示对于1~j,建了 i 个关键点,最后一个关键点在 j 最小花费
转移用线段树维护一下就好了, O ( n m l o g n )
套一个wqs二分可以做到 O ( n l o g n l o g P ) ,不过m不大这个m优化成log对整体速度影响不大

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
#define SZ(x) (int)x.size()
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;

const int maxn = 41000;

int n,m,U;
int dis[maxn];
int C[maxn],R[maxn],P[maxn];
vector< pair<int,int> >V[maxn];
ll rsum[maxn];

struct segment
{
    ll seg[maxn<<2],fl[maxn<<2];
    int segp[maxn<<2];
    void init(int x,const int l,const int r)
    {
        seg[x]=fl[x]=0; segp[x]=l;
        if(l==r) return;
        int mid=(l+r)>>1;
        init(lc,l,mid); init(rc,mid+1,r);
    }
    void pushdown(int x)
    {
        if(!fl[x])return;
        ll c=fl[x];fl[x]=0;
        seg[lc]+=c,seg[rc]+=c;
        fl[lc]+=c,fl[rc]+=c;
    }
    void pushup(int x)
    {
        int y=seg[lc]<=seg[rc]?lc:rc;
        seg[x]=seg[y],segp[x]=segp[y];
    }
    int lx,rx,c;
    void upd(int x,const int l,const int r)
    {
        if(rx<l||r<lx) return;
        if(lx<=l&&r<=rx) { seg[x]+=c,fl[x]+=c;return; }
        pushdown(x);
        int mid=(l+r)>>1;
        upd(lc,l,mid); upd(rc,mid+1,r);
        pushup(x);
    }
    ll nowmin;
    int nowre;
    void query(int x,const int l,const int r)
    {
        if(rx<l||r<lx) return;
        if(lx<=l&&r<=rx)
        {
            if(nowmin==-1||seg[x]<nowmin) nowmin=seg[x],nowre=segp[x];
            return;
        }
        pushdown(x);
        int mid=(l+r)>>1;
        query(lc,l,mid); query(rc,mid+1,r);
    }
}seg;

struct data
{
    ll f;
    int k;
}f[maxn],ans;
int dp(int mid)
{
    seg.init(1,0,n);

    f[0]=(data){0,0};
    for(int i=1;i<=n;i++)
    {
        seg.nowmin=-1;
        seg.lx=0,seg.rx=i-1,seg.query(1,0,n);

        f[i]=(data){seg.nowmin+mid+C[i],f[seg.nowre].k+1};
        seg.lx=seg.rx=i,seg.c=f[i].f,seg.upd(1,0,n);

        for(int j=0;j<SZ(V[i]);j++)
        {
            seg.lx=0,seg.rx=V[i][j].fir-1,seg.c=V[i][j].sec;
            seg.upd(1,0,n);
        }
    }
    ans.f=-1;
    for(int i=1;i<=n;i++)
    {
        f[i].f+=rsum[i+1];
        if(ans.f==-1||ans.f>f[i].f) ans=f[i];
    }
    return ans.k;
}

ll Solve()
{
    int l=0,r=U;
    ll re;
    while(l<=r)
    {
        int mid=((ll)l+r)>>1;
        if(dp(mid)<=m) r=mid-1,re=ans.f-(ll)mid*m;
        else l=mid+1;
    }
    return re;
}

int main()
{
    freopen("jhaha.in","r",stdin);
    freopen("jhaha.out","w",stdout);

    scanf("%d%d",&n,&m);
    for(int i=2;i<=n;i++) scanf("%d",&dis[i]);
    for(int i=1;i<=n;i++) scanf("%d",&C[i]);
    for(int i=1;i<=n;i++) scanf("%d",&R[i]);
    for(int i=1;i<=n;i++) scanf("%d",&P[i]),U+=P[i];

    for(int i=1;i<=n;i++)
    {
        int l=1,r=i;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(dis[i]-dis[mid]<=R[i]) r=mid-1;
            else l=mid+1;
        }
        int L=r+1; rsum[L]+=P[i];

        l=i,r=n;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(dis[mid]-dis[i]<=R[i]) l=mid+1;
            else r=mid-1;
        }
        V[l-1].pb(mp(L,P[i]));
    }
    for(int i=n-1;i>=1;i--) rsum[i]+=rsum[i+1];

    printf("%lld\n",Solve());

    return 0;
}

猜你喜欢

转载自blog.csdn.net/l_0_forever_lf/article/details/80820499