難題訓練(一)

「EC Final 2019」狄利克雷 k 次根

題意:給出一個函數的前nng(1),g(2)....g(n)g(1),g(2)....g(n),求它在狄利克雷卷積意義下的kk次根ff的前nn項,這裏的狄利克雷卷積是指h(n)=dnf(d)g(nd)modph(n) = \sum_{d|n} f(d)g(\frac nd)\bmod p

n1e5n \leq 1e5做法:
容易發現fp=ϵf^p = \epsilon,也就是狄利克雷卷積的單位元。
所以求出kmodpk \bmod p的逆元bb後即可直接gb=fkb=fap+1=fg^b = f^{kb} = f^{ap+1} = f
狄利克雷卷積快速冪即可。
CodeCode

#include<bits/stdc++.h>
#define maxn 1000005
#define mod 998244353
using namespace std;

int n,K,a[maxn],b[maxn];
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }

void mul(int *a,int *b,int *c){
	static int d[maxn];
	for(int i=1;i<=n;i++) d[i] = 0;
	for(int i=1;i<=n;i++) for(int j=1,k=i;k<=n;k+=i,j++)
		d[k] = (d[k] + 1ll * a[i] * b[j]) % mod;
	for(int i=1;i<=n;i++) c[i] = d[i];
}

int main(){
	scanf("%d%d",&n,&K);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	b[1] = 1;K = Pow(K , mod-2);
	for(;K;K>>=1,mul(a,a,a)) if(K&1) mul(a,b,b);
	for(int i=1;i<=n;i++) printf("%d%c",b[i]," \n"[i==n]);
} 

n1e6n \leq 1e6做法:
考慮狄利克雷生成函數F(x)=i=1nf(i)ixF(x) = \sum_{i=1}^n f(i)i^x
狄利克雷卷積就是他們的生成函數相乘。
對於gg的生成函數求ln\ln÷k\div kexp\exp即可。
lnF(x)=C+F(x)F(x)\ln F(x) = C + \int \frac {F(x)'}{F(x)}
其中CC是一個與xx無關的常數,是因爲對於常數aa顯然lnaaa\ln a \neq \int \frac {a'}a,所以需要一個C=lnaC=\ln a來補全。
但是這個題它的常數是f(1)1xf(1)1^x,這就很爲難了,你不能不說它是常數,但是他的確看上去和xx有關。
就這樣吧,C=lnf(1)1x=0C = \ln f(1)1^x = 0
F(x)=i=1nf(i)ixlniF(x)' = \sum_{i=1}^n f(i)i^x\ln i
同時可以得到
F(x)=i=2nf(i)ixlni+C\int F(x) = \frac {\sum_{i=2}^n f(i)i^x}{\ln i} + C
expF(x)=expF(x)×F(x)\exp F(x)' = \exp F(x) \times F(x)'
expF(x)=expF(x)×F(x)\exp F(x) = \int \exp F(x) \times F(x)'
因爲F(x)F(x)'不存在1x1^x項所以可以遞推出expF(x)\exp F(x)
最大的問題是如何處理lni\ln i
需要重新定義導數和ln\ln,而積分和exp\exp則分別是他們的逆運算。
新的定義是一種線性變換滿足鏈式法則(lnF(x)=F(x)F(x)\ln F(x) = \int \frac {F(x)'}{F(x)}
對於ln\ln需要滿足exp(lnkF(x))=F(x)k\exp(\ln kF(x)) = F(x)^k,所以需要滿足lnab=lna+lnb\ln ab = \ln a + \ln bln1=0\ln 1 = 0
對於此題令lna\ln aaa的質因子個數,F(x)=i=1nf(i)ixlniF(x)' = \sum_{i=1}^n f(i)i^x\ln i即可。

AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#define maxn 1000005
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
using namespace std;

int n,K,a[maxn],b[maxn];
int pr[maxn],vis[maxn],sg[maxn],cnt_pr;
int inv[maxn];

void ln(int *a){
	static int b[maxn];
	rep(i,1,n) b[i] = 1ll * a[i] * sg[i] % mod;
	rep(i,1,n){
		for(int j=2,k=2*i;k<=n;k+=i,j++)
			b[k] = (b[k] - 1ll * b[i] * a[j]) % mod;
		b[i] = 1ll * b[i] * inv[sg[i]] % mod;
	}
	rep(i,1,n) a[i] = b[i];
}

void exp(int *a){
	static int b[maxn];
	rep(i,1,n) b[i] = 1ll * a[i] * sg[i] % mod , a[i] = 0;
	a[1] = 1;
	rep(i,1,n){
		if(i>1) a[i] = 1ll * a[i] * inv[sg[i]] % mod;
		for(int j=2,k=2*i;k<=n;k+=i,j++)
			a[k] = (a[k] + 1ll * a[i] * b[j]) % mod;
	}
}

int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod;return r; }

int main(){
	scanf("%d%d",&n,&K);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	
	inv[0] = inv[1] = 1;
	rep(i,2,n){
		if(!vis[i]) pr[cnt_pr++] = i , sg[i] = 1;
		for(int j=0;pr[j] * i <= n;j++){
			vis[i * pr[j]] = 1;
			sg[i * pr[j]] = sg[i] + 1;
			if(i % pr[j] == 0) break;
		}
	}
	rep(i,2,20) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
	
	ln(a);K = Pow(K , mod-2);
	rep(i,1,n) a[i] = 1ll * a[i] * K % mod;
	exp(a);
	
	rep(i,1,n) printf("%d%c",a[i]," \n"[i==n]);
} 

2018 ACM-ICPC World Finals H .Single Cut of Failure

給出一個w×hw\times h的矩形,並給出若干條端點在矩形邊上且不和矩形的邊重合的線段,給出最少用幾條端點在矩形邊上的線段可以切割所有線段的方案。

發現每條線段都會被主對角線和副對角線之一切割,所以答案最多是22
將第ii條線段的兩個端點都標號爲ii,對於所有端點極角排序後求是否有一個長度爲nn的連續子序列中沒有相同標號的點即可。

AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#define maxn 2000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define db double
using namespace std;

int n,w,h;
int c[maxn],cnt[maxn],A[maxn],B[maxn];
db ang[maxn];
bool cmp(const int &u,const int &v){ return ang[u] < ang[v]; }

int main(){
	scanf("%d%d%d",&n,&w,&h);
	rep(i,1,n){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		::c[i*2-1] = i*2-1 , ::c[i*2] = i*2;
		ang[i*2-1] = atan2(b-h/2.0,a-w/2.0);
		A[i*2-1] = a , B[i*2-1] = b;
		ang[i*2] = atan2(d-h/2.0,c-w/2.0);
		A[i*2] = c , B[i*2] = d;
	}
	sort(c+1,c+1+2*n,cmp);
	c[0] = c[2*n] , c[2*n+1] = c[1];
	int j=1;
	rep(i,1,2*n){
		for(;cnt[(c[i]+1)/2] == 1;)
			cnt[(c[j++]+1)/2] = 0;
		cnt[(c[i]+1)/2] = 1;
		if(i - j + 1 == n){
			printf("%d\n",1);
			if(A[c[i]] == w) printf("%lf %lf ",(db)A[c[i]],B[c[i]]+0.5);
			else if(B[c[i]] == h) printf("%lf %lf ",A[c[i]]-0.5,(db)B[c[i]]);
			else if(A[c[i]] == 0) printf("%lf %lf ",(db)A[c[i]],B[c[i]]-0.5);
			else printf("%lf %lf ",A[c[i]]+0.5,(db)B[c[i]]);
			
			i = j-1;
			if(A[c[i]] == w) printf("%lf %lf\n",(db)A[c[i]],B[c[i]]+0.5);
			else if(B[c[i]] == h) printf("%lf %lf\n",A[c[i]]-0.5,(db)B[c[i]]);
			else if(A[c[i]] == 0) printf("%lf %lf\n",(db)A[c[i]],B[c[i]]-0.5);
			else printf("%lf %lf\n",A[c[i]]+0.5,(db)B[c[i]]);
			return 0;
		}
	}
	printf("%d\n",2);
	printf("0.5 0 %lf %lf\n",w-0.5,(db)h);
	printf("%lf %lf %lf %lf\n",0.5,(db)h,w-0.5,0.0);
} 

2018 ACM-ICPC World Finals I. Triangles

給出一個圖,數等邊三角形個數。
維護往左,往左上,往右上的最長能走多遠,
然後對於每一排橫着掃,用BIT在(x,y)(x,y)+1+1,移動到(x+D,y)(x+D,y)1-1,其中DD(x,y)(x,y)往右上能走的最長距離,以此求出以(a,b)(a,b)爲右下角的三角形個數。
注意還需要處理以(a,b)(a,b)爲左上角的三角形個數。

AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define pb push_back
using namespace std;
 
int r,c;
char s[6005][12005];
int U[6005][12005],D[6005][12005];
int tr[24005];
vector<int>G[24005];
 
void upd(int u,int v){ for(;u<=4*c;u+=u&-u) tr[u] += v; }
int qry(int u){ int r=0;for(;u;u-=u&-u) r+=tr[u];return r; }
 
int main(){
	scanf("%d%d\n",&r,&c);
	rep(i,1,2*r-1)
		fgets(s[i]+1,2*c+2,stdin);
	LL ans = 0;
	queue<int>q;
	for(int i=1;i<=2*r-1;i+=2){
		for(int j=(i/2&1?3:1);j<=2*c-1;j+=4){
			if(s[i][j-1] == ' '){
				for(;!q.empty();q.pop()){
					int x = q.front();
					if(x + D[i][x] >= j) 
						upd(2 * c - x , -1) , G[x+D[i][x]].clear();
				}
			}
			if(s[i-1][j-1] != ' ' && s[i-1][j-1])
				U[i][j] = U[i-2][j-2] + 1;	
			else 
				U[i][j] = 0;
			if(s[i-1][j+1] != ' ' && s[i-1][j+1])
				D[i][j] = D[i-2][j+2] + 4;
			else 
				D[i][j] = 0;
			ans += qry(2*c-(j-4*U[i][j]));
			q.push(j);
			upd(2*c-j,1);
			G[j+D[i][j]].pb(j);
			for(;!G[j].empty();)
				upd(2*c-G[j].back(),-1),G[j].pop_back();
		}
		for(;!q.empty();q.pop()){
			int x = q.front();
			if(x + D[i][x] > 2*c-1) 
				upd(2 * c - x , -1) , G[x+D[i][x]].clear();
		}
	}	
	for(int i=2*r-1;i>=1;i-=2){
		for(int j=(i/2&1?3:1);j<=2*c-1;j+=4){
			if(s[i][j-1] == ' '){
				for(;!q.empty();q.pop()){
					int x = q.front();
					if(x + D[i][x] >= j) 
						upd(2 * c - x , -1) , G[x+D[i][x]].clear();
				}
			}
			if(s[i+1][j-1] != ' ' && s[i+1][j-1])
				U[i][j] = U[i+2][j-2] + 1;	
			else 
				U[i][j] = 0;
			if(s[i+1][j+1] != ' ' && s[i+1][j+1])
				D[i][j] = D[i+2][j+2] + 4;
			else 
				D[i][j] = 0;
			ans += qry(2*c-(j-4*U[i][j]));
			q.push(j);
			upd(2*c-j,1);
			G[j+D[i][j]].pb(j);
			for(;!G[j].empty();)
				upd(2*c-G[j].back(),-1),G[j].pop_back();
		}
		
		for(;!q.empty();q.pop()){
			int x = q.front();
			if(x + D[i][x] > 2*c-1) 
				upd(2 * c - x , -1) , G[x+D[i][x]].clear();
		}
	}	
	
	printf("%lld\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章