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
#include<bits/stdc++.h>
#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<pam.p;i++) //節點數實際到p-1
{
ans+=t[i]*sz[i]-1;
}
}
int T;
int main()
{
scanf("%d",&T);
for(int i=1;i<=T;i++)
{
ans=0;
scanf("%s",a);
pam.init();
int len=strlen(a);
for(int i=0;i<len;i++)
{
pam.add(a[i]);
}
cont();
printf("Case #%d: %lld\n",i,ans);
}
return 0;
}
E:Snowy Smile
題意 以座標的形式給你n個點的位置 和值 求最大子矩陣和 -1e9<=x[i],y[i],a[i]<=1e9。
參考to the max 的做法固定一維 枚舉另一維,但是 如果求最大字段和的地方跑dp 肯定要n方的複雜度。
所以這道題 在於怎麼降掉這個n ,由於點數只有(n<=2000) 個 我們可以考慮用線段樹維護最大子段和。然後每次
只要在離散化以後對每個x插入這維所有的y 然後t[1].dat直接求出當前的最大子段和。由於最多插入O(n)次,加上
外面枚舉的複雜度 平攤複雜度爲
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cmath>
#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;
vector<int>vec[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<<mp[i][j]<<endl;
}
}
}
ll maxl=-1e15;
//cout<<len1<<" "<<len2<<endl;
for(int i=1;i<=len1;i++)
{
memset(d, 0, sizeof(d));
build(1,1,2000);
for(int j=i;j<=len1;j++)
{
for(int k=0;k<vec[j].size();k++)
{
d[vec[j][k]]+=mp[j][vec[j][k]];
change(1,vec[j][k],d[vec[j][k]]);
}
ll temp=t[1].dat;
if(temp>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
*/