2019杭电多校第六场(C/E)

C:求出本质不同的回文串中能构成一个串为另一个串的子串所有字符串对的pair数

做法: 考虑每个点和他的fail点的贡献 , 在偶回文树和奇回文树上分别dfs, 求出该点和他的子节点的size, 然后该点的贡献就等于该点和他的fail点的未标记数,因为某个点和他的fail 点一定是该点的子节点的子串,那为什么不在fail链上继续往前找,因为根据回文树的性质,在一个节点fail链上往前找最后一定会找到该点的父节点。而该节点的父节点会在该点之前被计算,所以不用重复计算,但是一个点的fail点是一定不会是该点的父节点的,所以我们每次只要找这个点和他的fail节点就可以遍历所有点了,并且如果贡献为1 则一定是 该点未被标记而该点的fail点被标记

则此时的 ans=该点的size*1-1  为该点和他的所有子节点所构成的pair

若贡献为2 则ans=该点的size*2-1 为他的fail点与该点和所有子节点构成的pair + 该点和他的所有子节点所构成的pair

所以只要dfs一遍,每次统计该点的size ,标记该点和他的fail点计算每点的贡献,最后从节点2到p-1 

ans+=size[x]*t[x]-1

#include
#define ll long long
#define maxn 100010   //节点个数
using namespace std;
char a[maxn];
int vis[maxn];
struct pam
{
    int len[maxn];  //len[i] 节点i的回文串长度
    int nxt[maxn][26]; //nxt[i]['c'] 节点i的回文串在两边添加字符c后变成的回文串编号
    int fail[maxn]; //fail[i] 指向i的最长回文后缀所在的节点 且不为i
    int cnt[maxn]; //cnt[i] 节点i表示的回文串在S中出现的次数
    int s[maxn]; //s[i] 第i次添加的字符
    int num[maxn];
    int last; //指向以字符串中上一个位置结尾的回文串的节点
    int cur; //指向由next构成的树中当前回文串的父亲节点(即当前回文串是cur左右两边各拓展一个字符得来)
    int p; // 添加的节点个数
    int n; // 添加的字符串个数
    int newnode(int l)  //新建节点
    {
        for(int i=0;i<=25;i++)nxt[p][i]=0; // 消除子节点
        cnt[p]=num[p]=0;  //节点p为新回文串所以出现次数为0
        len[p]=l;
        return p++;
    }
    inline void init()
    {
        p=n=last=0;
        newnode(0);  //偶节点
        newnode(-1); //奇节点
        s[0]=-1;
        fail[0]=1;
    }
    int get_fail(int x) //找到可以插入的节点
    {
        while(s[n-1-len[x]]!=s[n])x=fail[x];
        return x;
    }
    inline void add(int c)
    {
        c-='a';
        s[++n]=c;
        int cur=get_fail(last); // 找到可以插入的节点并当做父节点
        if(!nxt[cur][c])
        {
            int now=newnode(len[cur]+2);
            fail[now]=nxt[get_fail(fail[cur])][c]; //从父节点的回文后缀开始找,找到一个s[l-1]=s[n]的点则出边的点为最长回文后缀
            nxt[cur][c]=now;
            num[now]=num[fail[now]]+1;
        }
        last=nxt[cur][c];  //成为新的上一个位置
        cnt[last]++;
    }
    void Count() //统计本质相同的回文串的出现次数。与位置无关
    {
        for(int i=p-1;i>=0;i--)
        {
            cnt[fail[i]]+=cnt[i]; //每个节点会比父节点先算完,于是父节点能加到所有的子节点
        }
    }
}pam;
ll ans=0;
int sz[maxn];
int t[maxn];
void dfs(int x)
{
    t[x]=(vis[x]==0)+(vis[pam.fail[x]]==0);
    sz[x]=1;
    vis[x]++;
    vis[pam.fail[x]]++;
    for(int i=0;i<26;i++)
    {
        if(pam.nxt[x][i]==0)continue;
        dfs(pam.nxt[x][i]);
        sz[x]+=sz[pam.nxt[x][i]];
    }
    vis[x]--;
    vis[pam.fail[x]]--;
}
void cont()
{
    dfs(0);
    dfs(1);
    for(int i=2;i

 

E:Snowy Smile

题意 以坐标的形式给你n个点的位置 和值 求最大子矩阵和 -1e9<=x[i],y[i],a[i]<=1e9。

参考to the max 的做法固定一维 枚举另一维,但是 如果求最大字段和的地方跑dp 肯定要O(n^{2})n方的复杂度。

所以这道题 在于怎么降掉这个n ,由于点数只有(n<=2000) 个 我们可以考虑用线段树维护最大子段和。然后每次

只要在离散化以后对每个x插入这维所有的y 然后t[1].dat直接求出当前的最大子段和。由于最多插入O(n)次,加上

外面枚举的复杂度 平摊复杂度为O(n^{2}log(n))

#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
int n;
int a[3010];
int x[3010];
int X1[3010];
int y[3010];
int Y1[3010];
ll d[3010];
int len1,len2;
ll mp[2010][2010];
const int maxn=2005;
vectorvec[2004];
struct segment
{
    int l,r;
    ll sum;
    ll rmax;
    ll lmax;
    ll dat;
}t[maxn*4];
ll Max(ll a, ll b, ll c)
{
    return max(a,max(b,c));
}
void pushup(int o)
{
    t[o].sum=t[o<<1].sum+t[o<<1|1].sum;
    t[o].lmax=max(t[o<<1].lmax, t[o<<1].sum+t[o<<1|1].lmax);
    t[o].rmax=max(t[o<<1|1].rmax, t[o<<1|1].sum+t[o<<1].rmax);
    t[o].dat=Max(t[o<<1].dat,t[o<<1|1].dat,t[o<<1].rmax+t[o<<1|1].lmax);
}
inline void build(int o, int l, int r)
{
    t[o].l=l;t[o].r=r;
    if(l==r)
    {
        t[o].lmax=d[l];
        t[o].rmax=d[l];
        t[o].sum=d[l];
        t[o].dat=d[l];
        return;
    }
    int mid=(l+r)>>1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    pushup(o);
}
inline void change(int o, int x, ll d)
{
    if(t[o].r==t[o].l)
    {
        t[o].lmax=t[o].rmax=t[o].sum=t[o].dat=d;
        return ;
    }
    int mid=(t[o].l+t[o].r)>>1;
    if(x<=mid)change(o<<1, x, d);
    else change(o<<1|1, x, d);
    pushup(o);
}
void discreat()
{
    sort(X1+1,X1+1+n);
    len1=unique(X1+1,X1+1+n)-X1-1;
    sort(Y1+1,Y1+1+n);
    len2=unique(Y1+1,Y1+1+n)-Y1-1;
}
int query1(int temp)
{
    return lower_bound(X1+1,X1+1+len1,temp)-X1;
}
int query2(int temp)
{
    return lower_bound(Y1+1,Y1+1+len2,temp)-Y1;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(mp,0,sizeof(mp));
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d",&x[i],&y[i],&a[i]);
            X1[i]=x[i],Y1[i]=y[i];
        }
        discreat();
        memset(d,0,sizeof(d));
        build(1,1,2000);
        for(int i=1;i<=n;i++)
        {
            mp[query1(x[i]) ][ query2(y[i]) ]+=a[i];
        }
        for(int i=1;i<=len1;i++)
        {
            for(int j=1;j<=len2;j++)
            {
                if(mp[i][j]!=0)
                {
                    vec[i].push_back(j);
                    //cout<maxl)maxl=temp;
            }
            vec[i].clear();
        }
        if(maxl<0)printf("0\n");
        else printf("%lld\n",maxl);
    }
    return 0;
}
/*
2
4
1 1 50
2 1 50
1 2 50
2 2 -500
2
-1 1 5
-1 1 1
*/

 

你可能感兴趣的:(个人,补题,题解,2019杭电多校)