FJWC2020 Day2
T3 number
维护正整数可重集,每次插入/删除一个数,每次询问 \(\gcd=k\) 的 \((i,j)\) 对数(无序且 \(i\not=j\))。
\(n,V\le 10^5\)。
考虑答案变化量:
令 \(x'=\dfrac{x}{k}\):
\(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\)。
转移有:
\(f_{t,0,0}+f_{t,0,1}+f_{t,1,0}\) 为所求,矩乘即可。
注意 \(t=1\) 时这个式子是没有问题的,因为此时只有第 \(1\) 组,它只能不染色。
答案
时间复杂度 \(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;
}

浙公网安备 33010602011771号