20190903训练记录

Codeforces 504E

题意是给一棵树,每个节点上一个字母,m次询问a到b路径的字符串和c到d路径的字符串的lcp.

n = 300000 , m = 1000000 n=300000,m=1000000 n=300000,m=1000000

看上去非常蠢的一个倍增题,一开始我觉得自己想明白了,甚至还质疑了为什么这道题能有3000分。

实际上一个 l o g log log的纯倍增还是漏洞重重的。

其中一个漏洞就是你可以 O ( 1 ) O(1) O(1)得到 v v v往上 2 k 2^k 2k的点,但是你没办法得到 v v v往下 2 k 2^k 2k的点。

不过此时我们得到了一个 l o g 2 log^2 log2的做法,试了一下,光荣TLE.

这个方法有改进的地方,就是得到 v v v往下 2 k 2^k 2k的点可以通过BSGS的思想维护每个点向上 k , 70 k , 4900 k ( 0 &lt; k ≤ 70 ) k,70k,4900k(0&lt;k \le 70) k,70k,4900k(0<k70)的点,这样就能 O ( 1 ) O(1) O(1)查询了。 但是还是TLE了

最后改成单哈希7000多ms卡过。

#include<bits/stdc++.h>
#define LL long long
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
using namespace std;
inline int read(){
	int v=0,f=1;
	char c=getchar();
	while (c<'0' || c>'9'){
		if (c=='-') f=-1;
		c=getchar();
	}
	while (c>='0' && c<='9'){
		v=v*10+c-'0';
		c=getchar();
	}
	return v*f;
}
const int Maxn=300005;
const LL mod1=1e9+9;
const LL mod2=998244353;
int n;
vector<int> G[Maxn];
char c[Maxn];
LL U1[Maxn][20],D1[Maxn][20];
//LL U2[Maxn][20],D2[Maxn][20];
LL pw1[Maxn];//,pw2[Maxn];
int par[Maxn][20],st[Maxn],ed[Maxn],kc,dep[Maxn];
int up1[Maxn][71],up2[Maxn][71],up3[Maxn][71];
bool ispar(int u,int v){
	return u==-1 || (st[u]<=st[v] && ed[v]<=ed[u]);
}
int lca(int u,int v){
	if (ispar(u,v)) return u;
	for (int i=19;i>=0;i--){
		if (!ispar(par[u][i],v)){
			u=par[u][i];
		}
	}
	return par[u][0];
}
void dfs(int x,int p){
	st[x]=++kc;
	par[x][0]=p;
	U1[x][0]=D1[x][0]=c[x];
	//U2[x][0]=D2[x][0];
	for (int i=0;i<G[x].size();i++){
		int v=G[x][i];
		if (v!=p){
			dep[v]=dep[x]+1;
			dfs(v,x);
		}
	}
	ed[x]=kc;
}
int lb[Maxn];
int Dv4[Maxn],Dv7[Maxn],Mv4[Maxn],Mv7[Maxn];
int Up(int x,int d){
	x=up3[x][Dv4[d]];
	d=Mv4[d];
	x=up2[x][Dv7[d]];
	d=Mv7[d];
	x=up1[x][d];
	return x;
}
//
int Solve(int x1,int y1,int x2,int y2){
	int l1=lca(x1,y1),l2=lca(x2,y2);
	//x1->l1 vs x2->l2
	int d1=dep[x1]-dep[l1]+1,d2=dep[x2]-dep[l2]+1;
	int f1=0,f2=0;
	int ans=0,curi=19;
	//cout<<d1<<' '<<d2<<endl;
	while (1){
		if (curi==-1) break;
		int cl=1<<curi;
		if (cl>d1 || cl>d2){
			curi--;
			continue;
		}
		LL H1,H2;
		if (!f1){
			H1=U1[x1][curi];//*mod2+U2[x1][curi];
		}
		else{
			int rd=d1-cl;
			int tmp=Up(y1,rd);
			H1=D1[tmp][curi];//*mod2+D2[tmp][curi];
		}
		if (!f2){
			H2=U1[x2][curi];//*mod2+U2[x2][curi];
		}
		else{
			int rd=d2-cl;
			int tmp=Up(y2,rd);
			H2=D1[tmp][curi];//*mod2+D2[tmp][curi];
		}
		
		if (H1!=H2){
			curi--;
			continue;
		}
	//	cout<<"fuck"<<' '<<curi<<endl;
		ans+=cl;
		
		bool ffff=0;
		if (!f1){
			d1-=cl;
			if (d1==0){
				f1=1;
				d1=dep[y1]-dep[l1];
				if (!d1) break;
				x1=Up(y1,d1-1);
				ffff=1;
			}
			else{
				x1=par[x1][curi];
			}
		}
		else{
			d1-=cl;
			if (d1==0){
				break;
			}
			x1=Up(y1,d1-1);
		}
		if (!f2){
			d2-=cl;
			if (d2==0){
				f2=1;
				d2=dep[y2]-dep[l2];
				if (!d2) break;
				x2=Up(y2,d2-1);
				ffff=1;
			}
			else{
			//	cout<<"FFF"<<' '<<x2<<' '<<curi<<endl;
				x2=par[x2][curi];
			}
		}
		else{
			d2-=cl;
			if (d2==0){
				break;
			}
			x2=Up(y2,d2-1);
		}
		curi--;
		if (ffff) curi=19;
	//	cout<<x1<<' '<<x2<<' '<<f1<<' '<<f2<<endl;
	}
	return ans;
}
int main(){
	for (int i=0;i<Maxn;i++){
		Dv4[i]=i/4900,Mv4[i]=i%4900,Dv7[i]=i/70,Mv7[i]=i%70;
	}
	scanf("%d",&n);
	scanf("%s",c+1);
	for (int i=0;i<n-1;i++){
		int u,v;
		u=read();v=read();
		G[u].pb(v);G[v].pb(u);
	}
	pw1[0]=1;//=pw2[0]=1;
	for (int i=1;i<Maxn;i++){
		pw1[i]=pw1[i-1]*114514191%mod1;
	//	pw2[i]=pw2[i-1]*1145141919%mod2;
	}
	dfs(1,-1);
	for (int i=1;i<=n;i++){
		up1[i][0]=i;
		for (int j=1;j<=70;j++){
			if (up1[i][j-1]==-1) up1[i][j]=-1;
			else up1[i][j]=par[up1[i][j-1]][0];
		}
	}
	for (int i=1;i<=n;i++){
		up2[i][0]=i;
		for (int j=1;j<=70;j++){
			if (up2[i][j-1]==-1) up2[i][j]=-1;
			else up2[i][j]=up1[up2[i][j-1]][70];
		}
	}
	for (int i=1;i<=n;i++){
		up3[i][0]=i;
		for (int j=1;j<=70;j++){
			if (up3[i][j-1]==-1) up3[i][j]=-1;
			else up3[i][j]=up2[up3[i][j-1]][70];
		}
	}
	for (int i=1;i<20;i++){
		for (int j=1;j<=n;j++){
			if (par[j][i-1]==-1){
				par[j][i]=-1;
			}
			else{
				int Md=par[j][i-1],Sz=1<<(i-1);
				par[j][i]=par[Md][i-1];
				U1[j][i]=(U1[j][i-1]*pw1[Sz]+U1[Md][i-1])%mod1;
			//	U2[j][i]=(U2[j][i-1]*pw2[Sz]+U2[Md][i-1])%mod2;
				D1[j][i]=(D1[Md][i-1]*pw1[Sz]+D1[j][i-1])%mod1;
			//	D2[j][i]=(D2[Md][i-1]*pw2[Sz]+D2[j][i-1])%mod2;
			}
		}
	}
	int q;
	scanf("%d",&q);
	while (q--){
		int x1,x2,y1,y2;
		x1=read();y1=read();x2=read();y2=read();
		printf("%d\n",Solve(x1,y1,x2,y2));
	}
	return 0;
}

Codeforces 932G

题意:

Given a string s, find the number of ways to split s to substrings such that if there are k substrings (p_1, p_2, p_3, ..., p_k) in partition, then p_i = p_{k - i + 1} for all i (1 ≤ i ≤ k) and k is even.

看了题想了一会,往border方面上想了一会,不会做。

题解的做法非常巧妙。

考虑把字符串按照 s 0 , s n − 1 , s 1 , s n − 2 , . . . s_0,s_{n-1},s_1,s_{n-2},... s0,sn1,s1,sn2,...的方式重排成另外一个字符串,那么原来的问题就变成了新的字符串有多少种划分成偶回文串的方法。(这个转化太神仙了,orz)

考虑 d p dp dp,有 f i = ∑ j ∣ s j + 1... i i s p a l i n d r o m e f j f_i=\sum_{j|s_{j+1...i}ispalindrome}f_j fi=jsj+1...iispalindromefj

暴力做是 O ( n 2 ) O(n^2) O(n2) 的,考虑用回文树来优化这个过程。

我们发现回文树中的border也和普通字符串的border有着相似的性质,一个回文串的回文border其实也是由log个等差数列组成的。这个时候我们维护每一个等差数列,向右推维护 d p dp dp的和即可O(1)求出对于一组border的dp和。

注意不要统计长度为奇数的回文串。
代码懒得写了(有时间再补
题解代码:

#include <bits/stdc++.h>
#define ll          long long
#define pb          push_back
#define mp          make_pair
#define pii         pair<int,int>
#define vi          vector<int>
#define all(a)      (a).begin(),(a).end()
#define F           first
#define S           second
#define sz(x)       (int)x.size()
#define hell        1000000007
#define endl        '\n'
using namespace std;
const int MAXN=1000005;
string s;
struct PalindromicTree{
    int N,cur;
    vector<map<int,int>> next;
    vector<int> link,start,len,diff,slink;
    PalindromicTree(): N(0),cur(0){
        node();
        len[0]=-1;
        node();
    }
    int node(){
        next.emplace_back();
        link.emplace_back(0);
        start.emplace_back(0);
        len.emplace_back(0);
        diff.emplace_back(0);
        slink.emplace_back(0);
        return N++;
    }
    void add_letter(int idx){
        while(true){
            if(idx-len[cur]-1>=0 && s[idx-len[cur]-1]==s[idx])
                break;
            cur=link[cur];
        }
        if(next[cur].find(s[idx])!=next[cur].end()){
            cur=next[cur][s[idx]];
            return;
        }
        node();
        next[cur][s[idx]]=N-1;
        len[N-1]=len[cur]+2;
        start[N-1]=idx-len[N-1]+1;
        if(len[N-1]==1){
            link[N-1]=diff[N-1]=slink[N-1]=1;
            cur=N-1;
            return;
        }
        while(true){
            cur=link[cur];
            if(idx-len[cur]-1>=0 && s[idx-len[cur]-1]==s[idx])
                break;
        }
        link[N-1]=next[cur][s[idx]];
        diff[N-1]=len[N-1]-len[link[N-1]];
        if(diff[N-1]==diff[link[N-1]])
        	slink[N-1]=slink[link[N-1]];
        else
        	slink[N-1]=link[N-1];
        cur=N-1;
    }
};
ll dp[MAXN],sans[MAXN];
int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	PalindromicTree pt;
	int i,cur;
	string str;
	cin>>str;
	for(i=0;i<sz(str)/2;i++){
		s.pb(str[i]);
		s.pb(str[sz(str)-i-1]);
	}
	dp[0]=1;
	for(i=1;i<=sz(s);i++){
		pt.add_letter(i-1);
		for(cur=pt.cur;cur>1;cur=pt.slink[cur]){
			sans[cur]=dp[i-pt.len[pt.slink[cur]]-pt.diff[cur]];
			if(pt.diff[cur]==pt.diff[pt.link[cur]])
				sans[cur]=(sans[cur]+sans[pt.link[cur]])%hell;
			dp[i]=(dp[i]+sans[cur])%hell;
		}
		if(i&1)
			dp[i]=0;
	}
	cout<<dp[sz(s)];
	return 0;
}

总时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值