【省選模擬】20/02/09

題目 ptfptf

  • T1:\text{T}1:
    nn 個點帶標號每個連通塊大小 A\le A 的森林個數
    暴力 dpdpdpi=j=1Adpij(i1ij)jj2dp_i=\sum_{j=1}^{A}dp_{i-j}\binom{i-1}{i-j}j^{j-2}
    然後發現可以直接 expexpF(x)=exp(i=0Aii2j!)F(x)=exp(\sum_{i=0}^A\frac{i^{i-2}}{j!})
    然後就寫了個分治 nttntt……
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = getchar();
	return cnt * f; 
}
cs int N = 1e5 + 50;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a, b); }
void Dec(int &a, int b){ a = dec(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
int ksm(int a, int b){ int as=1; for(;b;b>>=1,a=mul(a,a)) if(b&1) as=mul(as,a); return as; }
int n, A, coe[N], fac[N], ifac[N];
int inv(int a){ return a?mul(ifac[a],fac[a-1]):1; }
int C(int n, int m){ if(n<0||m<0||n<m) return 0; return mul(fac[n],mul(ifac[n-m],ifac[m])); }
void fac_init(int n){
	fac[0] = fac[1] = ifac[0] = ifac[1] = coe[1] = coe[2] = 1;
	for(int i=1; i<=n; i++) fac[i] = mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],Mod-2);
	for(int i=n-1; i>=2; i--) ifac[i]=mul(ifac[i+1],i+1);
	for(int i=3; i<=n; i++) coe[i]=ksm(i,i-2);
}
#define poly vector<int> 
cs int K = 18; 
poly w[K+1];
void NTT_init(){
	for(int i=1; i<=K; i++) w[i].resize(1<<(i-1));
	int wn=ksm(3,(Mod-1)/(1<<K)); w[K][0]=1;
	for(int i=1; i<(1<<(K-1)); i++) w[K][i]=mul(w[K][i-1],wn);
	for(int i=K-1;i;i--) for(int j=0;j<(1<<(i-1));j++) w[i][j]=w[i+1][j<<1];
}
int f[N], g[N];
int up, bit; poly rev;
void init(int deg){
	up=1; bit=0; while(up<deg) up<<=1,++bit; rev.resize(up);
	for(int i=0; i<up; i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}
void NTT(poly &a, int typ){
	for(int i=0;i<up;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=1,l=1; i<up; i<<=1,++l)
	for(int j=0; j<up; j+=(i<<1))
	for(int k=0; k<i; k++){
		int x=a[k+j], y=mul(w[l][k],a[k+j+i]);
		a[k+j]=add(x,y); a[k+j+i]=dec(x,y);
	}
	if(typ==-1){
		reverse(a.begin()+1,a.end());
		for(int i=0,iv=ksm(up,Mod-2);i<up;i++) Mul(a[i],iv);
	}
}
poly operator * (poly a, poly b){
	int deg = a.size()+b.size()-1; init(deg);
	a.resize(up); b.resize(up); NTT(a,1); NTT(b,1);
	for(int i=0; i<up; i++) Mul(a[i],b[i]); NTT(a,-1);
	a.resize(deg); return a;
}
void work(int l, int r){
	if(l==r) return;
	int mid = (l+r) >> 1;
	work(l,mid);
	poly A, B;
	for(int i=l;i<=mid;i++) A.pb(f[i]);
	B.pb(0); for(int i=1;i<=r-l;i++) B.pb(g[i]);
	A = A * B;
	for(int i=mid+1;i<=r;i++) Add(f[i],mul(inv(i),A[i-l]));
	work(mid+1,r);
}
int main(){
	freopen("forest.in","r",stdin);
	freopen("forest.out","w",stdout);
	n = read(), A = read(); fac_init(n); NTT_init();
	for(int i=1; i<=A; i++) g[i]=mul(ifac[i-1],coe[i]);
	f[0]=1; work(0,n); cout<<mul(fac[n],f[n]); return 0;
}
  • T2:\text{T}2:
    一個字符串,問有多少 S[l,r]S_{[l,r]} 滿足其可以分成相同的 KK 段,n3e5n\le 3e5
    將問題轉換一下就是 lcs(i,j)(ji)(k1)lcs(i,j)\ge (j-i)*(k-1),於是考慮建出 SAMSAM 然後枚舉 i,lcai,lca
    lcalca 處查詢除 ii 子樹中滿足 j(i,i+lenk1]j\in(i,i+\lfloor\frac{len}{k-1}\rfloor] 的個數
    不能暴力跳,考慮鏈分治,將一個詢問 ii 放到若干條重鏈上,每條重鏈需要做一個前綴,即將一個前綴的輕兒子全部插入,那麼我們就從上往下做,暴力插入輕兒子
    但注意到這裏的 lenlen 不一樣,轉換一下發現就是 i[jlenk1,j)i\in [j-\lfloor\frac{len}{k-1}\rfloor,j),這樣每一個子樹的 lenlen 就是一樣的了,所以就是區間加單點查詢,然後到鏈尾要特殊處理,發現就是一個子樹查詢
    考場腦子抽了寫了個線段樹合併,其實離線下來用一個樹狀數組加差分就可以實現子樹查詢
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = getchar();
	return cnt * f; 
}
cs int N = 6e5 + 50;
typedef long long ll;
int n, K, ps[N], rt[N]; char S[N];
ll Ans;
namespace SGT{
	cs int N = ::N * 40;
	int ls[N], rs[N], sz[N], nd;
	#define mid ((l+r)>>1)
	void ins(int &x, int l, int r, int p){
		if(!x) x = ++nd; ++sz[x]; if(l == r) return;
		(p<=mid)?ins(ls[x],l,mid,p):ins(rs[x],mid+1,r,p);
	}
	int merge(int x, int y){
		if(!x||!y) return x|y; int nx=++nd; 
		ls[nx]=merge(ls[x],ls[y]);
		rs[nx]=merge(rs[x],rs[y]); 
		sz[nx]=sz[ls[nx]]+sz[rs[nx]]; return nx;
	}
	int query(int x, int l, int r, int L, int R){
		if(!x) return 0; if(L<=l&&r<=R) return sz[x]; int as=0;
		if(L<=mid) as+=query(ls[x],l,mid,L,R);
		if(R>mid) as+=query(rs[x],mid+1,r,L,R); return as;
	}
	int qry(int x, int l, int r){
		if(l>n) l=n; if(r>n) r=n; if(l>r) return 0;
		return query(x,1,n,l,r);
	}
}
namespace SAM{
	int ch[N][26],lk[N],len[N],r[N],nd=1,las=1;
	int extend(int c, int k){
		int now=++nd, p=las; len[now]=len[p]+1; r[now]=k; 
		for(;p&&!ch[p][c];p=lk[p]) ch[p][c]=now;
		if(!p) lk[now] = 1; 
		else{
			int q = ch[p][c]; 
			if(len[q] == len[p]+1) lk[now] = q;
			else{
				int cl=++nd; len[cl]=len[p]+1; lk[cl]=lk[q];
				memcpy(ch[cl],ch[q],sizeof(ch[q]));
				for(;p&&ch[p][c]==q; p=lk[p]) ch[p][c]=cl;
				lk[now]=lk[q]=cl;
			}
		} las = now; return now;
	}
	void radix_sort(){
		static int bin[N], A[N];
		for(int i=1; i<=nd; i++) bin[len[i]]++;
		for(int i=1; i<=n; i++) bin[i]+=bin[i-1];
		for(int i=nd; i>=1; i--) A[bin[len[i]]--]=i;
		for(int i=nd; i>=2; i--){
			int u=A[i]; 
			if(r[u]) Ans+=(ll)SGT::qry(rt[u],r[u],r[u]+len[u]/(K-1));
			if(r[u]) SGT::ins(rt[u],1,n,r[u]);
			rt[lk[u]]=SGT::merge(rt[lk[u]],rt[u]);
		}
	}
}
using SAM::len;
using SAM::r;
vector<int> G[N];
vector<int> v[N];
int fa[N], top[N], son[N], sz[N];
void pre_dfs(int u){
	sz[u] = 1;
	for(int v : G[u]){
		fa[v] = u; pre_dfs(v); sz[u] += sz[v];
		if(sz[son[u]] < sz[v]) son[u] = v; 
	}
}
void dfs(int u, int tp){
	top[u] = tp; if(son[u]) dfs(son[u], tp);
	for(int v : G[u]) if(v ^ son[u]) dfs(v, v);
}
void ins(int x, int c){
	while(x){
		v[x].pb(c), x=top[x];
		if(fa[x]){
			Ans-=SGT::qry(rt[x],c+1,c+len[fa[x]]/(K-1));
			Ans+=SGT::qry(rt[fa[x]],c+1,c+len[fa[x]]/(K-1));
		} x=fa[x];
	}
}
namespace BIT{
	int c[N];
	void add(int x, int v){ if(x<1) x=1; if(x>n) return; for(;x<=n;x+=x&-x) c[x]+=v; }
	int ask(int x){ int as=0; for(;x;x-=x&-x) as+=c[x]; return as; }
}
void subwork(int u, int deg, int c){
	if(r[u]) BIT::add(r[u]-deg,c),BIT::add(r[u]+1,-c);
	for(int v : G[u]) subwork(v, deg, c);
}
void work(int u){
	for(int x = u; x; x = son[x])
		for(int v : G[x]) if(v ^ son[x]) work(v);
	for(int x = u; x; x = son[x]){
		for(int c : v[x]) Ans += (ll)BIT::ask(c);
		for(int v : G[x]) if(v ^ son[x]) subwork(v,len[x]/(K-1),1);
	}
	for(int x = u; x; x = son[x])
		for(int v : G[x]) if(v ^ son[x]) subwork(v,len[x]/(K-1),-1);
}
int main(){
	freopen("sutoringu.in","r",stdin);
	freopen("sutoringu.out","w",stdout);
	n = read(); K = read(); scanf("%s",S+1);
	for(int i=1; i<=n; i++) ps[i]=SAM::extend(S[i]-'a',i);
	SAM::radix_sort();
	for(int i=2; i<=SAM::nd; i++) G[SAM::lk[i]].pb(i);
	pre_dfs(1); dfs(1,1);
	for(int i = 1; i <= n; i++) ins(ps[i],i); 
	work(1); cout<<Ans; return 0;
}
  • T3:\text{T}3:
    求等腰直角三角形的個數
    枚舉兩個點搞出第三個點,考場用 mapmap 跑了 5 點幾秒嚇死,改成 setset 快了很多
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = getchar();
	return cnt * f; 
}
cs int N = 3e3 + 50;
typedef pair<int,int> pi;
int n, x[N], y[N];
struct node{ 
	int x, y; mutable int v; node(int _x=0, int _y=0, int _v=0){ x=_x; y=_y; v=_v; }
	bool operator < (cs node &a)cs{ return x==a.x?y<a.y:x<a.x; }
};
set<node> S; 
typedef set<node>::iterator It;
int main(){
	freopen("triangle.in","r",stdin);
	freopen("triangle.out","w",stdout);
	n = read();
	for(int i=1; i<=n; i++){
		x[i]=read(), y[i]=read(); It it=S.find(node(x[i],y[i]));
		if(it==S.end()) S.insert(node(x[i],y[i],1));
		else ++it->v;
	}
	int as = 0;
	for(int i=1; i<=n; i++)
	for(int j=i+1; j<=n; j++){
		int X=x[i]+x[j]+y[i]-y[j];
		int Y=y[i]+y[j]+x[j]-x[i];
		if(!(X&1)&&!(Y&1)){
			X>>=1, Y>>=1;
			It it=S.find(node(X,Y));
			if(it!=S.end()) as+=it->v;
		}
		X=x[i]+x[j]+y[j]-y[i];
		Y=y[i]+y[j]+x[i]-x[j];
		if(!(X&1)&&!(Y&1)){
			X>>=1, Y>>=1;
			It it=S.find(node(X,Y));
			if(it!=S.end()) as+=it->v;
		}
	} cout<<as; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章