2019-2020 ICPC Asia Xuzhou Regional Contest【徐州现场赛】

题目:

209-2020 ICPC Asia Xuzhou Regional Onsite Contest

E. Multiply

题意:

找到最大的 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 的幂为(阶乘分解质因子来算):

X_z=\left \lfloor \frac{a_1}{p} \right \rfloor+\left \lfloor \frac{a_1}{p^2} \right \rfloor+...+\left \lfloor \frac{a_1}{p^{c_1}} \right \rfloor+...+\left \lfloor \frac{a_n}{p} \right \rfloor+\left \lfloor \frac{a_n}{p^2} \right \rfloor+...+\left \lfloor \frac{a_n}{p^{c_n}} \right \rfloor

y!分解后 p 的幂为:

X_y=\left \lfloor \frac{y}{p} \right \rfloor+\left \lfloor \frac{y}{p^2} \right \rfloor+...+\left \lfloor \frac{y}{p^{c}} \right \rfloor

又有:

C_i<C,\left \lfloor \frac{a_1}{p^x} \right \rfloor+\left \lfloor \frac{a_2}{p^x} \right \rfloor+...+\left \lfloor \frac{a_n}{p^x} \right \rfloor<=\left \lfloor \frac{y}{p^x} \right \rfloor

得证:

X_z<=X_y

代码:

#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;
}

H. Yuuki and a problem

题意:

给定一个长度为 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;
}

 

M. Kill the tree

题意:

咕咕咕

分析:

根据定义,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;
}

 

你可能感兴趣的:(比赛----题解)