209-2020 ICPC Asia Xuzhou Regional Onsite Contest
找到最大的 i 使得 z*x^i 是 y! 的因子
题目有说:a1+a2+......+an < y,那么 z 一定是 y!的因子[1],所以答案一定存在;如果 a 是 b 的因子,那么必有:将两个数唯一分解后,a 的每个质因子的幂 <= b 相同的质因子的幂;那么做法就呼之欲出了,将 x 唯一分解后,找到每个质因子对应的最大的 i,然后取最小值便是最后的答案;x 的范围比较大,套个 Pollard_rho 模板就做完了
[1]简单证明:
还是考虑两边质因子分解,首先 y!包含 z 具有的所有质因子,那么只用看每个质因子的幂;设 z 分解后的某一个质因子为 p, z 中 p 的幂为(阶乘分解质因子来算):
y!分解后 p 的幂为:
又有:
得证:
#include
using namespace std;
typedef long long LL;
const int maxn = 1e5+16;
LL x,y,a[maxn];
struct BigIntegerFactor{
const static int maxm = 1e6+16;
LL prime[maxm],p[maxm],fac[maxm],sz,cnt; //多组输入注意初始化cnt = 0
inline LL mul(LL a,LL b,LL mod){ //WA了尝试改为__int128或慢速乘
if(mod <= 1000000000) return a * b % mod;
return (a*b-(LL)((long double)a/mod*b+1e-8)*mod+mod)%mod;
}
void init(int maxn){
int tot = 0; sz = maxn-1;
for(int i = 1;i <= sz; ++i) p[i] = i;
for(int i = 2;i <= sz; ++i){
if(p[i] == i) prime[tot++] = i;
for(int j = 0;j>= 1;
}
return res;
}
bool check(LL a,LL n){ //二次探测原理检验n
LL t = 0,u = n-1;
while(!(u&1)) t++,u >>= 1;
LL x = powl(a,u,n),xx = 0;
while(t--){
xx = mul(x,x,n);
if(xx==1 && x!=1 && x!=n-1) return false;
x = xx;
}
return xx == 1;
}
bool miller(LL n,int k){
if(n == 2) return true;
if(n < 2 || !(n&1)) return false;
if(n <= sz) return p[n] == n;
for(int i = 0;i <= k; ++i){ //测试k次
if(!check(rand()%(n-1)+1,n)) return false;
}
return true;
}
inline LL gcd(LL a,LL b){
return b == 0 ? a : gcd(b,a%b);
}
inline LL Abs(LL x){
return x < 0 ? -x : x;
}
LL Pollard_rho(LL n){ //基于路径倍增的Pollard_Rho算法
LL s = 0,t = 0,c = rand()%(n-1)+1,v = 1,ed = 1;
while(1){
for(int i = 1; i <= ed; ++i){
t = (mul(t,t,n) + c) % n; v = mul(v,Abs(t-s),n);
if(i % 127 == 0){
LL d = gcd(v,n);
if(d > 1) return d;
}
}
LL d = gcd(v,n); if(d > 1) return d;
s = t; v = 1; ed <<= 1;
}
}
void getfactor(LL n){ //得到所有的质因子(可能有重复的)
if(n <= sz){
while(n != 1) fac[cnt++] = p[n],n /= p[n];
return;
}
if(miller(n,6)) fac[cnt++] = n;
else{
LL d = n; while(d >= n) d = Pollard_rho(n);
getfactor(d); getfactor(n/d);
}
}
LL cal(LL n,LL x){ //计算 n! 中质因子 x 的数量
LL num = 0;
while(n){
num += n/x;
n = n/x;
}
return num;
}
LL solve(int n,LL x,LL y){
map mp; LL ans = 4e18;
cnt = 0; getfactor(x);
for(int i = 0;i < cnt; ++i) mp[fac[i]]++;
map::iterator it = mp.begin();
while(it != mp.end()){
LL num = 0;
for(int i = 1;i <= n; ++i){
num += cal(a[i],it->first);
}
ans = min(ans,(cal(y,it->first)-num)/it->second);
it++;
}
return ans;
}
}Q;
int main(){
Q.init(100000);
int T,n; scanf("%d",&T);
while(T--){
scanf("%d %lld %lld",&n,&x,&y);
for(int i = 1;i <= n; ++i) scanf("%lld",a+i);
printf("%lld\n",Q.solve(n,x,y));
}
return 0;
}
给定一个长度为 N 的数列,有两种操作:(1)修改 A[x] = y(2)查询区间【L,R】内的数的所有子集和中最小没出现的正整数
如果给定一些数,让你求所有其所有子集和的 mex,大致做法就是从小到大排序,设前面能得到【1,x】内的数,添加下一个数 Ai,又可以得到【Ai,Ai+x】,如果 x < Ai -1,显然答案就是:x + 1;那么就可以建立主席树,每次查询从左子树到右子树去模拟这个过程;权值线段树维护区间出现的和,修改操作套个树状数组就好了
#include
#define x first
#define y second
#define pii pair
#define sz(x) (int)(x).size()
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 2e5+5;
const int mod = 998244353;
int n,q,x,y,op,qL,qR,a[maxn];
int tot,bit[maxn],root[maxn];
struct tree{
int l,r;
LL sum;
}T[maxn*100];
void build(int L,int R,int x,int &y,int pos,int val,int op){
if(op){T[++tot] = T[x]; T[tot].sum += val; y = tot;}
else{if(!y) y = ++tot; T[y].sum += val;} //树状数组不需要可持久化,动态开点就好
if(L == R) return ;
int mid = (L+R) >> 1;
if(pos > mid) build(mid+1,R,T[x].r,T[y].r,pos,val,op);
else build(L,mid,T[x].l,T[y].l,pos,val,op);
}
inline int lowbit(int x){
return x & (-x);
}
void updata(int x,int pos,int val){
while(x <= n){
build(1,maxn,bit[x],bit[x],pos,val,0);
x += lowbit(x);
}
}
bool flag;
int nxt[2][30][30],len0,len1;
void query(int L,int R,int x,int y,int dep,LL &sum){
if(flag) return ;
LL tep = T[y].sum-T[x].sum;
for(int i = 0;i < len0; ++i) tep += T[nxt[0][dep][i]].sum;
for(int i = 0;i < len1; ++i) tep -= T[nxt[1][dep][i]].sum;
if(tep == 0 || sum >= R-1){ //这一步使得每次查询复杂度近似为O(logNlogN)
sum += tep; return ;
}
if(L == R){ flag = true; return ;}
for(int i = 0;i < len0; ++i) nxt[0][dep+1][i] = T[nxt[0][dep][i]].l;
for(int i = 0;i < len1; ++i) nxt[1][dep+1][i] = T[nxt[1][dep][i]].l;
query(L,(L+R)>>1,T[x].l,T[y].l,dep+1,sum);
for(int i = 0;i < len0; ++i) nxt[0][dep+1][i] = T[nxt[0][dep][i]].r;
for(int i = 0;i < len1; ++i) nxt[1][dep+1][i] = T[nxt[1][dep][i]].r;
query(((L+R)>>1)+1,R,T[x].r,T[y].r,dep+1,sum);
}
int main(){
scanf("%d %d",&n,&q);
for(int i = 1;i <= n; ++i){
scanf("%d",a+i);
build(1,maxn,root[i-1],root[i],a[i],a[i],1);
}
while(q--){
scanf("%d",&op);
if(op == 1){
scanf("%d %d",&x,&y);
updata(x,a[x],-a[x]);
a[x] = y; updata(x,a[x],a[x]);
}
else{
scanf("%d %d",&qL,&qR);
len0 = len1 = 0;
for(int i = qR;i > 0;i -= lowbit(i)) nxt[0][1][len0++] = bit[i];
for(int i = qL-1;i > 0;i-=lowbit(i)) nxt[1][1][len1++] = bit[i];
LL sum = 0; flag = false;
query(1,maxn,root[qL-1],root[qR],1,sum);
cout << sum + 1 << '\n';
}
}
return 0;
}
咕咕咕
根据定义,the critical point 一定是树的重心,否则从重心每移动一步,值会越来越大;那就转化成求一棵有根树每个子树的重心,根据重心的定义可知,两颗子树合并后的重心一定在两颗子树的重心的简单路径上,这就可以从每个儿子节点的重心向上跳,来得到父亲节点的重心,实际上重心一定在重儿子的重心到根的路径上,如果在轻儿子,选根为重心一定会更优;注意树的重心可能有两个
#include
#define x first
#define y second
#define pii pair
#define SZ(x) (int)(x).size()
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 2e5+25;
int n,a,b;
int head[maxn],tot;
struct edge{
int to,nxt;
}e[maxn<<1];
inline void add(int u,int v){
e[++tot] = (edge){v,head[u]};
head[u] = tot;
}
int sz[maxn],maxsz[maxn],up[maxn];
void dfs1(int x,int fa){
sz[x] = 1; maxsz[x] = 0; up[x] = fa;
for(int i = head[x];i > 0;i=e[i].nxt){
int v = e[i].to; if(v == fa) continue;
dfs1(v,x); sz[x] += sz[v];
if(sz[v] > maxsz[x]) maxsz[x] = sz[v];
}
}
vector ans[maxn];
void dfs2(int x,int fa){
bool flag = false;
for(int i = head[x];i > 0;i=e[i].nxt){
int v = e[i].to; if(v == fa) continue;
dfs2(v,x); flag = true;
}
if(!flag) ans[x].push_back(x);
else{
ans[x].push_back(x); int sum = maxsz[x];
for(int i = head[x];i > 0;i=e[i].nxt){
int v = e[i].to; if(v == fa) continue;
int p = ans[v][0],maxsize = max(maxsz[p],sz[x]-sz[p]);
do{
if(maxsize < sum){
ans[x].clear();
ans[x].push_back(p); sum = maxsize;
}
else if(maxsize == sum) ans[x].push_back(p);
p = up[p]; int tep = Max(maxsz[p],sz[x]-sz[p]);
if(tep <= maxsize) maxsize = tep;
else break; //此时再向上跳已经没有意义
}while(p != x);
}
}
}
int main(){
scanf("%d",&n);
for(int i = 1;i < n; ++i){
scanf("%d %d",&a,&b);
add(a,b); add(b,a);
}
dfs1(1,0); dfs2(1,0);
for(int i = 1;i <= n; ++i){
if(SZ(ans[i]) == 1) printf("%d\n",ans[i][0]);
else printf("%d %d\n",Min(ans[i][1],ans[i][0]),Max(ans[i][1],ans[i][0]));
}
return 0;
}