Codeforces 1051E Vasya and Big Integers&1051F The Shortest Statement

1051E. Vasya and Big Integers

题意

给出三个大整数\(a,l,r\),定义\(a\)的一种合法的拆分为把\(a\)表示成若干个字符串首位相连,且每个字符串的大小在\(l,r\)之间,求每个字符串不能有前导零,求\(a\)有多少种合法的拆分方案。

题解

不难想到\(dp\),设\(dp_i\)表示前\(i\)个数有多少种合法的拆分方案。

\(dp_i=\sum_{j=1}^i dp_{j-1}\)(从\(j+1~i\)拆分的数在\(l,r\)之间)

直接转移是\(O(n^2)\),因为如果新拆分的数的长度小于\(l\)的长度,或者大于\(r\)的长度的话,那肯定是不合法的,所以我们考虑把转移分成三种。

1.新拆分的数的长度等于\(l\)的长度。

2.新拆分的数的长度等于\(r\)的长度。

3.新拆分的数的长度在\([l+1,r-1]\)这个区间里。

对于第三种,随便开个变量xjb维护下就行了。

对于前两种,问题就转换成了比较两个串的字典序。

比较两个字符串字典序的大小,就是相当于比较两个串\(lcp\)的后一位字符的大小。

\(lcp\)?我一眼\(SA\),经机房大佬提醒,突然想到可以写\(hash\)+二分减少代码量其实是太久没写SA忘了SA怎么写了,然后就被卡自然溢出到怀疑人生。

\(cf\)上一定不能写自然溢出!\(cf\)上一定不能写自然溢出!

#include<bits/stdc++.h>
#define For(i,x,y) for (register int i=(x);i<=(y);i++)
#define Dow(i,x,y) for (register int i=(x);i>=(y);i--)
#define cross(i,k) for (register int i=first[k];i;i=last[i])
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
    ll x=0;int ch=getchar(),f=1;
    while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
    if (ch=='-'){f=-1;ch=getchar();}
    while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
const int N = 1e6+10;
int na,nl,nr;
char a[N],l[N],r[N];
inline void rd(){
	scanf("%s%s%s",a+1,l+1,r+1);
	na=strlen(a+1),nl=strlen(l+1),nr=strlen(r+1);
}
const ull base = 2333;
const ll mod = 998244353;
ull p[N],hashl[N],hashr[N],hasha[N];
ll P[N],Hashl[N],Hashr[N],Hasha[N];
inline void init(){
	p[0]=1;
	For(i,1,na) p[i]=p[i-1]*base;
	For(i,1,nl) hashl[i]=hashl[i-1]*base+l[i];
	For(i,1,nr) hashr[i]=hashr[i-1]*base+r[i];
	For(i,1,na) hasha[i]=hasha[i-1]*base+a[i];
	P[0]=1;
	For(i,1,na) P[i]=P[i-1]*base%mod;
	For(i,1,nl) Hashl[i]=(Hashl[i-1]*base+l[i])%mod;
	For(i,1,nr) Hashr[i]=(Hashr[i-1]*base+r[i])%mod;
	For(i,1,na) Hasha[i]=(Hasha[i-1]*base+a[i])%mod;
}
inline ull Queryl(int x){return hashl[x];}
inline ull queryl(int x){return Hashl[x];}
inline ull Queryr(int x){return hashr[x];}
inline ull queryr(int x){return Hashr[x];}
inline ull Querya(int l,int r){return hasha[r]-hasha[l-1]*p[r-l+1];}
inline ull querya(int l,int r){return (Hasha[r]-Hasha[l-1]*P[r-l+1]%mod+mod)%mod;}
ll sum,dp[N];
inline void Mod(ll &x){for (;x>=mod;x-=mod);}
inline bool checkl(int x){
	if (a[x]=='0'&&nl>1) return 0;
	int L=1,R=nl,mid,ans=0;
	while (L<=R) mid=L+R>>1,(Queryl(mid)==Querya(x,x+mid-1)&&queryl(mid)==querya(x,x+mid-1))?(ans=mid,L=mid+1):R=mid-1;
	return (ans==nl)?1:(a[x+ans]>l[ans+1]);
}
inline bool checkr(int x){
	if (a[x]=='0'&&nr>1) return 0;
	int L=1,R=nr,mid,ans=0;
	while (L<=R) mid=L+R>>1,(Queryr(mid)==Querya(x,x+mid-1)&&queryr(mid)==querya(x,x+mid-1))?(ans=mid,L=mid+1):R=mid-1;
	return (ans==nr)?1:(a[x+ans]<r[ans+1]);
}
inline void solve(){
	dp[0]=1,a[0]='0';
	For(i,nl,na){
		if (a[i-nl]!='0'&&nr-nl>1) Mod(sum+=dp[i-nl-1]);
		if (i-nr+1>0&&a[i-nr+1]!='0'&&nr-nl>1) Mod(sum=sum+mod-dp[i-nr]);
		dp[i]=sum;//printf("%d %lld\n",i,sum);
		if (nl==nr) Mod(dp[i]+=checkl(i-nl+1)*checkr(i-nr+1)*dp[i-nl]);
		else {
			Mod(dp[i]+=checkl(i-nl+1)*dp[i-nl]);
			if (i-nr+1>0) Mod(dp[i]+=checkr(i-nr+1)*dp[i-nr]);
		}
	}
	printf("%lld",dp[na]);
}
int main(){
	rd(),init(),solve();
}

1051F. The Shortest Statement

题意

给出一个无向连通图,给出\(q\)次询问,每次询问\(u->v\)的最短路,边数-点数<=20。

题解

看到联通,边数-点数<=20,一眼想到肯定是把这个图转成几棵树然后求两个在所有树上的距离的\(min\)

图怎么转成树?还要最短路?\(Dij\)的拓展好像就是一棵树来着。

凭感觉做题.jpg

首先我们先随便搞个点作为起点跑\(Dij\),两个点的最短路如果不是两个点在树上的距离的话,那肯定是就经过了一条非树边,所以我们把所有的非树边都抠出来,然后对非树边的结点为起点,再跑\(Dij\)建树。

最多非树边只会有\(21\)条,所以复杂度\(O(nlogn*21)\)

#include<bits/stdc++.h>
#define For(i,x,y) for (register int i=(x);i<=(y);i++)
#define Dow(i,x,y) for (register int i=(x);i>=(y);i--)
#define cross(i,k) for (register int i=first[k];i;i=last[i])
#define Cross(i,k) for (register int i=First[k];i;i=Last[i])
using namespace std;
typedef long long ll;
inline ll read(){
    ll x=0;int ch=getchar(),f=1;
    while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
    if (ch=='-'){f=-1;ch=getchar();}
    while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
const int N = 1e5+10;
int n,m,x,y,z;
int tot,first[N],last[N<<1],to[N<<1];
ll val[N<<1];
inline void Add(int x,int y,int z){to[++tot]=y,val[tot]=z,last[tot]=first[x],first[x]=tot;}
int Tot,First[N],Last[N],To[N];
ll Val[N];
inline void add(int x,int y,int z){To[++Tot]=y,Val[Tot]=z,Last[Tot]=First[x],First[x]=Tot;}
int cnt,rt[50];
bool vis[N];
inline void GetRt(int u,int fa){
	vis[u]=1;
	cross(i,u){
		int v=to[i];
		if (v==fa) continue;
		if (vis[v]){rt[++cnt]=u;continue;}
	}
	Cross(i,u) if (To[i]!=fa) GetRt(To[i],u);
}
struct Tree{
	int rt;
	int size[N],son[N],dep[N],fa[N];
	ll dis[N];
	inline void dfs(int u){
		size[u]=1,dep[u]=dep[fa[u]]+1;
		Cross(i,u){
			int v=To[i];
			if (v==fa[u]) continue;
			fa[v]=u,dis[v]=dis[u]+Val[i],dfs(v),size[u]+=size[v];
			if (size[v]>size[son[u]]) son[u]=v;
		}
	}
	int top[N];
	inline void dfs(int u,int Top){
		top[u]=Top;
		if (son[u]) dfs(son[u],Top);
		Cross(i,u) if (To[i]!=fa[u]&&To[i]!=son[u]) dfs(To[i],To[i]);
	}
	inline int LCA(int x,int y){
		for (;top[x]!=top[y];dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]);
		return dep[x]<dep[y]?x:y;
	}
	inline void Build(){dfs(rt),dfs(rt,rt);}
	inline ll Query(int x,int y){return dis[x]+dis[y]-dis[LCA(x,y)]*2;}
}t[50];
struct node{
	int u;ll val;
	inline bool operator < (const node &b)const{return val>b.val;}
};
priority_queue<node>q;
int fa[N];
ll dis[N],FaVal[N];
inline void Dij(int k,int s){
	For(i,1,n) dis[i]=1e18,vis[i]=0;
	dis[s]=fa[s]=0,q.push((node){s,0});
	while (!q.empty()){
		int u=q.top().u;q.pop();
		if (vis[u]) continue;vis[u]=1;
		cross(i,u){
			int v=to[i];
			if (dis[u]+val[i]<dis[v]) dis[v]=dis[u]+val[i],q.push((node){v,dis[v]}),fa[v]=u,FaVal[v]=val[i];
		}
	}
	Tot=0,memset(First,0,sizeof First);
	//printf("rt=%d ",s);For(i,1,n) printf("%d ",fa[i]);puts("");
	For(i,1,n) if (fa[i]) add(fa[i],i,FaVal[i]);
	t[k].rt=s,t[k].Build();
}
inline void Build(){For(i,1,cnt) Dij(i,rt[i]);}
int main(){
	//freopen("data.in","r",stdin);
	//freopen("m.out","w",stdout);
	n=read(),m=read();
	For(i,1,m) x=read(),y=read(),z=read(),Add(x,y,z),Add(y,x,z);
	//puts("");
	//rt[cnt=1]=1,GetRt(1,0),Build();
	rt[cnt=1],Dij(1,1),memset(vis,0,sizeof vis),GetRt(1,0),Build();
	//printf("rt:");For(i,1,cnt) printf("%d ",rt[i]);puts("");
	int q=read();
	while (q--){
		x=read(),y=read();ll ans=1e18;
		For(i,1,cnt) ans=min(ans,t[i].Query(x,y));
		printf("%lld\n",ans);
	}
}
/*
10 10
1 2 9
2 3 5
3 4 5
3 5 8
4 6 6
3 7 6
7 8 8
8 9 4
3 10 4
8 1 7
1
9 5
*/

我只会摸鱼

posted @ 2018-10-17 08:25  zykykyk  阅读(284)  评论(0编辑  收藏  举报