FJWC2020 Day2

T3 number

维护正整数可重集,每次插入/删除一个数,每次询问 \(\gcd=k\)\((i,j)\) 对数(无序且 \(i\not=j\))。

\(n,V\le 10^5\)


考虑答案变化量:

\[\sum_{i=1}^{V}c_i[\gcd(i,x)=k] \]

\(x'=\dfrac{x}{k}\)

\[\sum_{i=1}^{\lfloor\frac{V}{k}\rfloor}c_{ik}[\gcd(i,x')=1] \]

\[\sum_{p|x'}\mu(p)\sum_{i=1}^{\lfloor\frac{V}{pk}\rfloor}c_{ipk} \]

\(O(n\cdot \max\{\sigma(a)\})\) 维护即可。

#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define V 100000
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int q,k;
vector<int>e[N];
int p[N>>2],pcnt;bool vis[N];
int mu[N];
void init(){
	for(int i=1;i<=V;i++)
		for(int j=i;j<=V;j+=i)
			e[j].push_back(i);
	mu[1]=1;
	for(int i=2;i<=V;i++){
		if(!vis[i])p[++pcnt]=i,mu[i]=-1;
		for(int j=1;j<=pcnt&&i*p[j]<=V;j++){
			vis[i*p[j]]=true;
			if(i%p[j]==0)break;
			mu[i*p[j]]=-mu[i];
		}
	}
}
int cnt[N],sum[N];ll ans;
int calc(int x){
	int ret=0;
	for(int d:e[x])
		ret+=mu[d]*sum[k*d];
	return ret;
}
int main(){
	q=read(),k=read();
	init();
	for(int opt,x;q;q--){
		opt=read(),x=read();
		if(x%k){
			printf("%lld\n",ans);
			continue;
		}
		if(!opt){
			if(!cnt[x]){
				printf("%lld\n",ans);
				continue;
			}
			cnt[x]--;
			for(int i:e[x])sum[i]--;
			ans-=calc(x/k);
		}
		else{
			ans+=calc(x/k);
			cnt[x]++;
			for(int i:e[x])sum[i]++;
		}
		printf("%lld\n",ans);
	}

	return 0;
}

T1 building

给定 \(\{h_n\}\),初始有空数组 \(\{a_n\}\),可以不断执行如下操作:

  • 选择 \(i\in[1,n)\),令 \(a_i+1\)\(a_{i+1}+2\)
  • 选择 \(i\in[1,n)\),令 \(a_i+2\)\(a_{i+1}+1\)

问使 \(\forall i\in[1,n]\)\(a_i\geq h_i\)​ 的最小步数。

\(n,h\le 10^6\)


反悔贪心。

  • 对于执行过的 \(a_i+2\)\(a_{i+1}+1\) 的操作,考虑撤销,改为 \(a_i+1,a_{i+1}+2\) 两次,即令 \(a_i\) 不变,用 \(1\) 的代价令 \(a_{i+1}+3\)
  • 对于执行过的 \(a_i+3\) 的操作,考虑撤销,改为 \(a_i+1,a_{i+1}+2\)\(a_{i}+2,a_{i+1}+1\),即令 \(a_i\) 不变,用 \(1\) 的代价令 \(a_{i+1}+3\)
  • 对于执行过的 \(a_{i}+3\) 的操作,考虑撤销,改为 \(a_i+1,a_{i+1}+2\) 三次,即令 \(a_i\) 不变,用 \(2\) 的代价令 \(a_{i+1}+6\)

由于反悔须保证 \(a_i\) 不变,操作 \(a_i+1,a_{i+1}+2\) 不可撤销。

贪心使得靠前的 \(a_i\) 操作次数尽量小。记录用了几个 \(a_{i-1}+3\)\(a_{i-1}+2,a_{i}+1\),可知能用多少次 \(a_i+3\)。若次数用完仍有 \(a_i<h_i\),记 \(h_i-a_i=k\),用 \(\lfloor\dfrac{k}{2}\rfloor\)\(a_{i}+2,a_{i+1}+1\)\(k\bmod 2\)\(a_{i}+1,a_{i+1}+2\)

#include<bits/stdc++.h>
#define ll long long
#define N 1000010
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,h[N],a[N];
int t1,t2,t3;ll ans;
int main(){
	n=read();
	for(int i=1;i<=n;i++)h[i]=read();
	for(int i=1;i<=n;i++){
		h[i]=max(0,h[i]);
		t1=min(h[i]/3,a[i]),h[i]-=t1*3;
		t2=h[i]/2,h[i]-=t2*2;
		t3=h[i],h[i]-=t3;
		ans+=t1+t2+t3;
		a[i+1]+=t1*2+t2;
		h[i+1]-=t2+t3*2;
	}
	printf("%lld\n",ans);

	return 0;
}

T2 bracelet

给有 \(n\) 个珠子的环染色,每个珠子可以染上 \(k\) 种颜色之一或不染。相邻两个珠子不能同时染色。

求染色方案,旋转本质相同,模 \(10^9+7\)

\(n,k\le 10^9\)


Burnside 引理:\(\displaystyle L=\frac{1}{|G|}\sum_{g\in G}c1(g)\)

求旋转 \(i\) 个位置下的不动方案数,令 \(t=\gcd(n,i)\)\(t\) 相同的 \(i\) 可以归为一类,有 \(\varphi(\dfrac{n}{t})\) 个。

将珠子分为 \(t\) 组,每组 \(\dfrac{n}{t}\) 个,染色应满足:同组珠子必须同色,相邻组珠子不能同时染色(包括第 \(1\) 组和第 \(t\) 组)。

\(f_{i,0/1,0/1}\) 为前 \(i\) 个点,\(1\) 号点是否染色,\(i\) 号点是否染色的方案数。

初始值 \(f_{1,0,0}=1,f_{1,1,1}=k\)

转移有:

\[\begin{cases}f_{i,0,0}=f_{i-1,0,0}+f_{i-1,0,1} \\ f_{i,0,1}=k\cdot f_{i-1,0,0} \\ f_{i,1,0}=f_{i-1,1,0}+f_{i-1,1,1} \\ f_{i,1,1}=k\cdot f_{i-1,1,0}\end{cases} \]

\(f_{t,0,0}+f_{t,0,1}+f_{t,1,0}\) 为所求,矩乘即可。

注意 \(t=1\) 时这个式子是没有问题的,因为此时只有第 \(1\) 组,它只能不染色​​。

答案

\[\frac{1}{n}\sum_{t|n}\operatorname{calc}(t)\cdot \varphi(\frac{n}{t}) \]

时间复杂度 \(O(\sigma_0(n)\cdot \sqrt{n})\)

#include<bits/stdc++.h>
#define P 1000000007
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int qpow(int k,int b){
	int ret=1;
	while(b){
		if(b&1)ret=1ll*ret*k%P;
		k=1ll*k*k%P,b>>=1;
	}
	return ret;
}
struct Mat{
	int a[4][4];//写了8倍常数 :D
	Mat(){memset(a,0,sizeof(a));}
	Mat operator*(const Mat &b)const{
		Mat c;
		for(int i=0;i<4;i++)
			for(int k=0;k<4;k++)
				for(int j=0;j<4;j++)
					c.a[i][j]=(c.a[i][j]+1ll*a[i][k]*b.a[k][j])%P;
		return c;
	}
}init,base;
Mat mpow(int b){
	Mat ret=init,cur=base;
	while(b){
		if(b&1)ret=ret*cur;
		cur=cur*cur,b>>=1;
	}
	return ret;
}
int n,k;
int calc(int t){
	Mat res=mpow(t-1);
	return (1ll*res.a[0][0]+res.a[0][1]+res.a[0][2])%P;
}
int phi(int x){
	int ret=x;
	for(int i=2;i*i<=x;i++){
		if(x%i)continue;
		ret-=ret/i;
		while(x%i==0)x/=i;
	}
	if(x>1)ret-=ret/x;
	return ret;
}
int main(){
	n=read(),k=read();
	init.a[0][0]=1,init.a[0][3]=k;
	base.a[0][0]=base.a[1][0]=base.a[2][2]=base.a[3][2]=1;
	base.a[0][1]=base.a[2][3]=k;
	int ans=0;
	for(int i=1;i*i<=n;i++){
		if(n%i)continue;
		ans=(ans+1ll*calc(i)*phi(n/i))%P;
		if(i*i!=n)ans=(ans+1ll*calc(n/i)*phi(i))%P;
	}
	ans=1ll*ans*qpow(n,P-2)%P;
	printf("%d\n",ans);

	return 0;
}
posted @ 2024-02-16 19:48  SError  阅读(36)  评论(0)    收藏  举报