2020.8.13集训

math

description
d ( i ) d(i) d(i) i i i的因数个数
给定 n , m n,m n,m,求:
∑ i = 1 n ( − 1 ) ∑ j = 1 m d ( i ⋅ j ) \sum_{i=1}^n(-1)^{\sum_{j=1}^m d(i\cdot j)} i=1n(1)j=1md(ij)

n ≤ 1 0 7 , m ≤ 1 0 14 n\leq 10^7,m\leq 10^{14} n107,m1014

solution
发现我们只用关心有几个 j j j可以让 i ⋅ j i\cdot j ij是完全平方数
i = p q 1 2 i=pq_1^2 i=pq12,其中 p p p的质因数最高次幂是 1 1 1,那么任意承上一个 p q 2 2 pq_2^2 pq22之后, d ( i ⋅ j ) d(i\cdot j) d(ij)一定是完全平方数

f ( i ) f(i) f(i)表示 i i i最小乘上多少之后可以变成一个完全平方数,则 i i i的个数为 ⌊ ⌊ m f ( i ) ⌋ ⌋ \lfloor\sqrt{\lfloor\frac{m}{f(i)}\rfloor}\rfloor f(i)m ,最后一次统计答案就可以

利用线性晒可以 O ( n ) O(n) O(n)解决

code

#include 
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=1e7+5;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

ll n,m;
ll f[N];
ll ans;
int prime[N],tot;
bool isprime[N];

int main()
{
	freopen("math.in","r",stdin);
	freopen("math.out","w",stdout);
	memset(isprime,1,sizeof(isprime));
	read(n),read(m);	
	isprime[0]=isprime[1]=0;
	f[1]=1;
	Rep(i,2,n){
		if(isprime[i])prime[++tot]=i,f[i]=i;
		for(int j=1;j<=tot&&1ll*i*prime[j]<=n;j++){
			isprime[i*prime[j]]=false;
			if(i%prime[j]==0){
				if(f[i]%prime[j]==0)f[i*prime[j]]=f[i]/prime[j];
				else f[i*prime[j]]=f[i]*prime[j];
				break;	
			}
			else f[i*prime[j]]=f[i]*prime[j];
		}
	}
	Rep(i,1,n){
		ll val=m/f[i];
		val=floor(sqrt(1.*val));
		if(val&1)ans--;
		else ans++;	
	}
	printf("%lld\n",ans);
	return 0;
}

osu

description
n n n个点,第 i i i个在 t i t_i ti秒出现在 ( x i , y i ) (x_i,y_i) (xi,yi),初始从 ( 0 , 0 ) (0,0) (0,0)开始,问鼠标移动的速度的最大值最小是多少,使得可以点到 k k k个点

答案可以表示成 a b c \frac{a\sqrt b}{c} cab 的形式,其中 b b b不含平方因子, gcd ⁡ ( a , c ) = 1 \gcd(a,c)=1 gcd(a,c)=1

输出 a , b , c a,b,c a,b,c

n , k ≤ 2000 n,k\leq 2000 n,k2000

solution

发现可能的距离一共最多有 n 2 n^2 n2种,所以我们可以把他们都存到一个数组里面,按照距离排序,在这个数组上二分,check的时候dp一下就好了

复杂度 O ( n 2 log ⁡ n 2 ) O(n^2\log n^2) O(n2logn2)

code

#include 
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=2005;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int n,k;
int tot;
int f[N];

struct misaka{
	int x,y,t;
	bool operator < (const misaka &cmp)const{
		return t<cmp.t;
	}
}a[N];

struct mikoto{
	int a,c;
	bool operator < (const mikoto &cmp)const{
		return 1ll*a*cmp.c*cmp.c<1ll*cmp.a*c*c;
	}
}q[N*N];

int dist(int i,int j){
	return (a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y);
}

int gcd(int a,int b){
	if(!b)return a;
	return gcd(b,a%b);	
}

bool check(int x){
	memset(f,-0x3f,sizeof(f));
	f[0]=0;
	int res=0;
	Rep(i,1,n)
		Rep(j,0,i-1)
			if(1ll*dist(i,j)*q[x].c*q[x].c<=1ll*q[x].a*(a[i].t-a[j].t)*(a[i].t-a[j].t)){
				f[i]=max(f[i],f[j]+1);	
				res=max(res,f[i]);
			}
	return res>=k;
}

int main()
{
	freopen("osu.in","r",stdin);
	freopen("osu.out","w",stdout);
	read(n),read(k);
	Rep(i,1,n)read(a[i].t),read(a[i].x),read(a[i].y);
	sort(a+1,a+n+1);
	Rep(i,1,n)
		Rep(j,i+1,n){
			tot++;
			int fz=dist(i,j);
			q[tot].c=a[j].t-a[i].t;
			q[tot].a=fz;	
		}
	sort(q+1,q+tot+1);
	int l=1,r=tot,ans=0;
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid))ans=mid,r=mid-1;
		else l=mid+1;	
	}
	int a=1,b=q[ans].a,c=q[ans].c;
	for(int i=2;i*i<=b;i++)
		while(b%(i*i)==0)b/=(i*i),a*=i;
	int GCD=gcd(a,c);
	a/=GCD,c/=GCD;
	printf("%d %d %d\n",a,b,c);
	return 0;
}

map

description
给定一个 n n n个点 m m m条边的无向图,如果两点之间存在两条不相交的路径,那么我们说这个点对是安全的

现在给出 q q q次询问,每次询问在 ( x , y ) (x,y) (x,y)之间连上一条边之后,会增加多少个安全点对

n , q ≤ 2 × 1 0 5 , m ≤ 4 × 1 0 5 n,q\leq 2\times 10^5,m\leq 4\times 10^5 n,q2×105,m4×105

solution

显然缩点,考虑 ( x , y ) (x,y) (x,y)连边之后,任意两个不同的点双的两个点都会变成安全的,所以贡献是

∑ x ( ∑ x − x ) = ( ∑ x ) 2 − ∑ x 2 \sum x(\sum x-x)=(\sum x)^2-\sum x^2 x(xx)=(x)2x2

所以在树上倍增预处理一下就好了

code

#include 
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=3e5+5;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int n,m,q;
int dfn[N],low[N],dfsxu,col[N],siz[N],dcc;
int fa[N],rt;
int f[N][20],dep[N];
ll g[N][20],h[N][20],ans;
bool bridge[N<<1];

int find(int x){
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);	
}

void merge(int x,int y){
	fa[find(x)]=find(y);	
}

struct graph{
	int head[N],cnt;
	graph(){
		memset(head,-1,sizeof(head));
		cnt=1;	
	}
	struct Edge{
		int to,next;
	}e[N<<1];
	void add(int x,int y){
		e[++cnt]=(Edge){y,head[x]},head[x]=cnt;
	}
}g1,g2;

void tarjan(int u,int fa){
	dfn[u]=low[u]=++dfsxu;
	bool flag=false;
	for(int i=g1.head[u];~i;i=g1.e[i].next){
		int v=g1.e[i].to;
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v])
				bridge[i]=bridge[i^1]=true;
		}
		else if(v!=fa||flag)low[u]=min(low[u],dfn[v]);
		else flag=true;
	}
}

void dfs1(int u){
	col[u]=dcc;
	siz[dcc]++;
	for(int i=g1.head[u];~i;i=g1.e[i].next){
		int v=g1.e[i].to;
		if(col[v]||bridge[i])continue;
		dfs1(v);
	}
}

void dfs2(int u,int fa){
	dep[u]=dep[fa]+1;
	f[u][0]=fa;
	g[u][0]=siz[u];
	h[u][0]=1ll*siz[u]*siz[u];
	Rep(i,1,19){
		f[u][i]=f[f[u][i-1]][i-1];
		g[u][i]=g[u][i-1]+g[f[u][i-1]][i-1];
		h[u][i]=h[u][i-1]+h[f[u][i-1]][i-1];	
	}
	for(int i=g2.head[u];~i;i=g2.e[i].next){
		int v=g2.e[i].to;
		if(v==fa)continue;
		dfs2(v,u);	
	}
}

ll lca(int x,int y){
	ll ans1=0,ans2=0;
	if(dep[x]<dep[y])swap(x,y);
	_Rep(i,19,0)
		if(dep[f[x][i]]>=dep[y]){
			ans1+=g[x][i];
			ans2+=h[x][i];
			x=f[x][i];	
		}
	if(x==y){
		ans1+=siz[x];
		ans2+=1ll*siz[x]*siz[x];
		return ans1*ans1-ans2;	
	}
	_Rep(i,19,0)
		if(f[x][i]!=f[y][i]){
			ans1+=g[x][i];
			ans2+=h[x][i];
			ans1+=g[y][i];
			ans2+=h[y][i];
			x=f[x][i],y=f[y][i];	
		}
	ans1+=g[x][0],ans2+=h[x][0];
	ans1+=g[y][0],ans2+=h[y][0];
	ans1+=siz[f[x][0]],ans2+=1ll*siz[f[x][0]]*siz[f[x][0]];
	return ans1*ans1-ans2;
}

int main()
{
	freopen("map.in","r",stdin);
	freopen("map.out","w",stdout);
	read(n),read(m),read(q);
	Rep(i,1,m){
		int x,y;
		read(x),read(y);
		g1.add(x,y),g1.add(y,x);
	}
	Rep(i,1,n)if(!dfn[i])tarjan(i,i);
	Rep(i,1,n)if(!col[i])dcc++,dfs1(i);
	Rep(i,1,dcc)fa[i]=i;
	Rep(i,2,g1.cnt)
		if(bridge[i]){
			int u=g1.e[i].to,v=g1.e[i^1].to;
			g2.add(col[u],col[v]);
			merge(col[u],col[v]);
		}
	int rt=dcc+1;
	Rep(i,1,dcc)if(fa[i]==i)g2.add(dcc+1,i),g2.add(i,dcc+1);
	dfs2(1,0);
	while(q--){
		int x,y;
		read(x),read(y);
		if(find(col[x])==find(col[y]))ans+=lca(col[x],col[y]);	
	}
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(比赛总结)