难题训练(一)

「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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章