1. 程式人生 > 實用技巧 >「考試總結」20200724初音

「考試總結」20200724初音

A.字串識別

Description

link

Solution

這題問題出在了對於一個字尾自動機,這裡如果在 \(DAG\) 上面進行操作,複雜度是 \(O(n^2)\)

所以我們換個思路做,還是考慮那些 \(endpos\) 集合大小為 \(1\) 的字串

看看貢獻:

\[i\in [i-len_{fa_i}+1,i]\ \ \ \ ans_i=min(ans_i,len_{fa_i}+1) \]

\[i\in[i-len_i+1,i-len_{fa_i}] \ \ \ \ ans_i=min(ans_i,len_p-i+1) \]

對於第一種情況,線段樹統計答案

對於第二種情況,觀察到 \(ans_i\)

這一項總是加著一個 \(i\)

那麼先統一加上,然後最後面減掉,對位置減就好的

另外,區間取 \(min\) 的線段樹,需要好好記得怎麼寫……

Code

#include<bits/stdc++.h>
using namespace std;
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return res*f;
	}
	const int N=5e5+10,M=15e5+10;
	int ans[N],le;
	struct SGT{
		int maxx[N<<2];
		#define maxx(p) maxx[p]
		inline void build(int p,int l,int r,int val)
		{
			if(l==r) return maxx(p)=le+l*val,void();
			int mid=(l+r)>>1; build(p<<1,l,mid,val); build(p<<1|1,mid+1,r,val);
			return maxx(p)=2e9+10,void();
		}
		inline void update(int p,int st,int ed,int l,int r,int val)
		{
			if(l<=st&&ed<=r) return maxx(p)=min(maxx(p),val),void();
			int mid=(st+ed)>>1; 
			if(l<=mid) update(p<<1,st,mid,l,r,val);
			if(r>mid) update(p<<1|1,mid+1,ed,l,r,val); 
			return ;
		}		
		inline void work(int p,int st,int ed,int val)
		{
			if(st==ed)
			{
				ans[st]=min(ans[st],maxx(p)-val*st);
				return ;
			} 
			int mid=(st+ed)>>1;
			maxx(p<<1)=min(maxx(p<<1),maxx(p));
			maxx(p<<1|1)=min(maxx(p<<1|1),maxx(p));
			work(p<<1,st,mid,val); work(p<<1|1,mid+1,ed,val);
			return ;
		}
	}T1,T2;
	struct edg{
		int to,nxt;
	}e[M<<1];
	int head[M],cnt;
	inline void add(int u,int v)
	{
		e[++cnt].to=v; e[cnt].nxt=head[u];
		return head[u]=cnt,void();
	}
	int son[M][26],fa[M],len[M],tot=1,r[M],las=1,sz[M];
	inline void copy(int x,int y)
	{
		for(int i=0;i<26;++i) son[x][i]=son[y][i];
		return ;
	} 
	inline void extend(int x,int id)
	{
		int tmp=las,np=las=++tot; len[np]=len[tmp]+1; sz[np]=1; r[np]=id;
		while(tmp&&!son[tmp][x]) son[tmp][x]=np,tmp=fa[tmp];
		if(!tmp) return fa[np]=1,void();
		int q=son[tmp][x];
		if(len[q]==len[tmp]+1) return fa[np]=q,void();
		int clone=++tot; len[clone]=len[tmp]+1;
		fa[clone]=fa[q],fa[np]=fa[q]=clone; copy(clone,q); r[clone]=x;
		while(tmp&&son[tmp][x]==q) son[tmp][x]=clone,tmp=fa[tmp];
 		return ;
	} 
	inline void dfs(int x)
	{
		for(int i=head[x];i;i=e[i].nxt)
		{
			int t=e[i].to; dfs(t); 
			sz[x]+=sz[t];
		}
		if(sz[x]==1) 
		{
			T1.update(1,1,le,r[x]-len[fa[x]]+1,r[x],len[fa[x]]+1);
			T2.update(1,1,le,r[x]-len[x]+1,r[x]-len[fa[x]],len[x]+1);
		}
		return ;
	}	
	char s[N];
	signed main()
	{
		memset(ans,0x3f,sizeof(ans));
		scanf("%s",s+1); le=strlen(s+1); 
		T1.build(1,1,le,0); 
		T2.build(1,1,le,1); 
		for(int i=1;i<=le;++i) extend(s[i]-'a',i); 
		for(int i=1;i<=tot;++i) add(fa[i],i); dfs(1);
		T1.work(1,1,le,0); 
		T2.work(1,1,le,1);
		for(int i=1;i<=le;++i) printf("%d\n",ans[i]);
		return 0;
	}
}
signed main(){return yspm::main();}

B. CQOI2015 選數

Descripion

\[\sum_{a_1=L}^R \sum_{a_2=L}^R\dots\sum_{a_n=L}^R [gcd(a_1,a_2,a_3,\dots,a_n)=k] \ \ (mod\ 10^9+7) \]

所有參量滿足 \(\le 10^9\)

Solution

式子真難看,反手一推變成

\[\sum_{i=1}^{\lfloor \frac R k\rfloor} \mu(i) (\lfloor \frac {\lfloor \frac R k\rfloor} i\rfloor-\frac {\lfloor \frac L k\rfloor} i\rfloor)^n \]

(考試的時候推錯式子了……然後外加一些手殘就爆零了)

其實搞幾個變數代替一下就沒這麼麻煩了……

然後我們發現這種做法需要開不下的空間做 \(\mu\)

然後去學了一下杜教篩就做完了

或者容斥大佬會這種做法

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf (int)1e18+10
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return res*f;
	}
	const int mod=1e9+7;
	inline int ksm(int x,int y)
	{
		int res=1; 
		for(;y;y>>=1,x=x*x%mod) if(y&1) res=res*x%mod;
		return res;
	}
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	const int N=7e6+10;
	bool fl[N];
	int pri[N],cnt,mu[N],n,l,h,k,ans,low,up;
	inline void prework()
	{
		mu[1]=1;
		for(int i=2;i<N;++i) 
		{
			if(!fl[i]) pri[++cnt]=i,mu[i]=mod-1;
			for(int j=1;j<=cnt&&i*pri[j]<N;++j) 
			{
				fl[i*pri[j]]=1;
				if(i%pri[j]==0){mu[i*pri[j]]=0; break;}
				else mu[i*pri[j]]=-mu[i],mu[i*pri[j]]=(mu[i*pri[j]]+mod)%mod;
			}
		} 
		for(int i=1;i<N;++i) mu[i]=add(mu[i],mu[i-1]);
		return ;
	}
	map<int,int> mp;
	inline void calc(int x,int &ans)
	{
		if(x<N) return ans=mu[x],void();
		if(mp[x]) return ans=mp[x],void();
		ans=1; 
		for(int l=2,r,s=0;l<=x;l=r+1)
		{
			r=x/(x/l); s=0;
			calc(x/l,s);
			(s+=mod)%mod;
			ans-=(r-l+1)*s%mod; 
			ans=add(ans,mod);
		} return mp[x]=ans,void();
	}
	inline int ask(int x)
	{
		int ans=0; calc(x,ans); return ans;
	}
	signed main()
	{
		n=read(); k=read(); l=read(); h=read();
		low=(l-1)/k; up=h/k;
		if(low>up) return puts("0"),0;
		prework(); 
		for(int i=1,j;i<=up;i=j+1)
		{
			j=min(low/i?low/(low/i):inf,up/(up/i));
			ans=add(ans,(ask(j)-ask(i-1)+mod)%mod*ksm(up/i-low/i,n)%mod);
		} cout<<ans<<endl;
		return 0;
	}
}
signed main(){return yspm::main();}

C.樹點塗色

\(sb\) 的一個題目了,不到二十天之前剛剛做完,然後今天就忘記了

不寫了,直接貼link

總結

感覺上午狀態很詭異,上手記錯了一個結論,線段樹寫了一個多小時,還妄想自己能 \(AK\) 結果呢?

\(20\)

好好的 \(lct\) 不會打,反演的式子還推錯

我服了我自己,慢慢考試慢慢進步吧