Comet OJ - Contest #9 & X Round 3题解

传送门

\(A\)

咕咕

typedef long long ll;
int a1,a2,n,d;ll res;
int main(){
    scanf("%d%d%d",&a1,&a2,&n);
    d=a2-a1;
    res=1ll*(a1+a1+1ll*(n-1)*d)*n>>1;
    printf("%lld\n",res);
    return 0;
}

\(B\)

不难发现答案要么是\(1\)要么是\(2\)

当且仅当\(k+1\)是个质数且范围内为\(k+1\)的倍数的数只有一个时,答案为\(1\)否则为\(2\)

据说\(n=2\)要特殊考虑,我是直接把\(n\leq 100\)的情况用暴力跑了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
ll n,k;
inline bool ck(R ll x){fp(i,2,sqrt(x))if(x%i==0)return false;return true;}
int vis[105];
void solve(){
    R int p=1;
    vis[k+1]=1;
    fp(res,1,2333){
        fp(i,2,n+1)if(vis[i]==res)
            fp(j,2,n+1)if(!vis[j])
                if(__gcd(i,j)==1)vis[j]=res+1,++p;
        if(p==n)return printf("%d\n",res),void();
    }
}
int main(){
    scanf("%lld%lld",&n,&k);
    if(n<=100)return solve(),0;
    puts(ck(k+1)&&(k+1)*2>n+1?"1":"2");
    return 0;
}

\(C\)

二分+点分

二分最远的距离,同时强制点分中心必选,且以点分中心为根

那么一个点会被选,当且仅当它的子树中有点被选,或者它的子树中离他最远的点到他的距离\(=mid\)(即二分出的最远距离),如果选的点数小于等于\(k\)说明合法

因为点分的时候会删点,所以记得处理来自删点后的子树内的最远距离

ps:据说这题直接倍增就可以过了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=5e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int sz[N],son[N],vis[N],ok[N],dis[N],las[N],rt,size;
int l,r,mid,ans,n,k,sum;bool fl;
inline int max(R int x,R int y){return x>y?x:y;}
void findrt(int u,int fa){
    sz[u]=1,son[u]=0;
    go(u)if(!vis[v]&&v!=fa)findrt(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
    cmax(son[u],size-sz[u]);
    if(son[u]<son[rt])rt=u;
}
void did(int u,int fa){
    ok[u]=0,dis[u]=las[u];
    go(u)if(!vis[v]&&v!=fa)did(v,u),cmax(dis[u],dis[v]+1),ok[u]|=ok[v];
    ok[u]|=(dis[u]>=mid),sum+=ok[u];
}
void solve(int u){
    vis[u]=1,sum=0;
    did(u,0);
    if(!ok[u])ok[u]=1,++sum;
    if(sum<=k)return fl=1,void();
    R int t1=0,t2=0,to=0;
    go(u)if(!vis[v]){
        if(dis[v]+1>=t2)t1=t2,t2=dis[v]+1,to=v;
        else cmax(t1,dis[v]+1);
    }
    if(las[u]>=t2)t1=t2,t2=las[u],to=0;
    else cmax(t1,las[u]);
    R int ss=size;
    go(u)if(!vis[v]){
        las[v]=v==to?t1+1:t2+1;
        if(las[v]>mid)continue;
        rt=0,size=sz[v]>sz[u]?size-sz[u]:sz[v],findrt(v,0),solve(rt);
        if(fl)return;
    }
}
int ck(){
    memset(las,0,(n+1)<<2);
    memset(vis,0,(n+1)<<2);
    fl=0,son[0]=n+1;
    size=n,rt=0,findrt(1,0),solve(rt);
    return fl;
}
int main(){
    scanf("%d%d",&n,&k);
    for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
    int l=1,r=n;
    while(l<=r){
        mid=(l+r)>>1;
        ck()?(ans=mid,r=mid-1):l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

\(D\)

考虑跳儿子的过程,如果是跳到一个轻儿子,那么子树大小减半,所以跳轻儿子的总次数不会超过\(O(log n)\)次,所以我们只需要考虑优化跳重儿子的过程即可

对于每一个点,我们记录它的重儿子在它的儿子中标号是多少,那么跳重儿子的话只要计算它的重链和\(a_i\)的\(LCP\)即可,可以用二分+\(Hash\)求出

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

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=5e5+5;
typedef unsigned long long ll;
vector<int>to[N];ll bas[N];
int n,q,m;
struct Bit{
    ll c[N];
    inline void upd(R int x,R ll y){y*=bas[x];for(;x<=N-5;x+=x&-x)c[x]+=y;}
    inline ll query(R int x){R ll res=0;for(;x;x-=x&-x)res+=c[x];return res;}
    inline ll query(R int l,R int r){return (query(r)-query(l-1))*bas[N-5-l];}
}T,G;
int sz[N],son[N],id[N],ed[N],dfn[N],rk[N],fa[N],a[N],deg[N],tim,rt;
void dfs1(int u){
    sz[u]=1,sort(to[u].begin(),to[u].end());
    R int c=0;
    for(auto v:to[u]){
        ++c,dfs1(v),sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])son[u]=v,id[u]=c;
    }
}
void dfs2(int u){
    dfn[u]=++tim,rk[tim]=u,ed[u]=u;
    if(!son[u])return T.upd(tim,-23333),void();
    dfs2(son[u]),ed[u]=ed[son[u]],T.upd(dfn[u],id[u]);
    for(auto v:to[u])if(v!=son[u])dfs2(v);
}
inline int min(R int x,R int y){return x<y?x:y;}
int LCP(R int li,R int ri,R int l,R int r){
    int lp=1,rp=min(r-l+1,ri-li+1),ans=0,mid;
    while(lp<=rp){
        mid=(lp+rp)>>1;
        T.query(li,li+mid-1)==G.query(l,l+mid-1)?(ans=mid,lp=mid+1):rp=mid-1;
    }
    return ans;
}
int solve(R int u,R int l,R int r){
    R int li=dfn[u],ri=dfn[ed[u]];
    while(233){
        R int len=LCP(li,ri,l,r);
        li+=len,l+=len;
        u=rk[li];
        if(l>r||deg[u]<a[l])return u;
        u=to[u][a[l]-1],li=dfn[u],ri=dfn[ed[u]],++l;
        if(l>r)return u;
    }
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read(),m=read(),q=read();
    bas[0]=1;fp(i,1,N-5)bas[i]=bas[i-1]*19260817;
    fp(i,1,n){
        fa[i]=read();
        if(fa[i])to[fa[i]].push_back(i),++deg[fa[i]];else rt=i;
    }
    dfs1(rt),dfs2(rt);
    fp(i,1,m)a[i]=read(),G.upd(i,a[i]);
    for(R int op,x,y,z;q;--q){
        op=read(),x=read(),y=read();
        if(op==2)G.upd(x,y-a[x]),a[x]=y;
        else z=read(),print(solve(x,y,z));
    }
    return Ot(),0;
}

\(E\)

用原根预处理之后\(O(1)\)计算\(i^i\),同时直接暴力合并路径计算答案即可,具体细节看代码

然后是关于复杂度的问题,若\(u\)为其中一个点,且\(u\)到某个叶子的路径上的点分别为\(u,p_1,p_2,p_3,...,p_x\),那么路径\((u,u),(u,p_1),(u,p_2),...,(u,p_x)\)中,不同的\(\&\)值只有\(O(\log a_i)\)种,所以假设是一条链,且以其中一个叶子为根,那么每一次把子树里的所有形如\((v,i)\)的路径变成\((u,i)\)的复杂度是\(O(\log a_i)\),那么记\(d\)为叶子个数,在不合并路径的情况下复杂度为\(O(nd\log a_i)\)

对于路径的合并,两个叶子只会在它们的\(LCA\)处被合并一次,所以这部分复杂度为\(O(d^2\log^2 a_i)\),不过似乎很难卡到上界(至少我是没找到能让它卡到上界的数据)

综上复杂度是没有问题的

//quming
#include<bits/stdc++.h>
#define R register
#define pb push_back
#define fi first
#define se second
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=786433,g=10;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
const int N=2e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void Add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int ind[P],fpow[P];
inline void init(){
    fpow[0]=1;
    for(R int x=g,i=1;x!=1;x=mul(x,g),++i)ind[x]=i,fpow[i]=x;
}
inline int calc(R int x,R int y){
    if(x%=P,!x)return 0;
    return fpow[1ll*ind[x]*y%(P-1)];
}
typedef pair<int,int> pi;
int a[N],n,res;
vector<pi> dfs(int u,int fa){
    vector<pi> now(1,pi(a[u],1));
    go(u)if(v!=fa){
        vector<pi> to=dfs(v,u),tt;
        for(auto &x:to)x.fi&=a[u];
        pi p(0,0);
        for(auto x:to)if(p.fi!=x.fi){
            if(p.fi)tt.pb(p);
            p=x;
        }else p.se+=x.se;
        if(p.fi)tt.pb(p);
        for(const auto &x:now)for(const auto &y:tt)
            upd(res,1ll*calc(x.fi&y.fi,x.fi&y.fi)*x.se*y.se%P);
        now.insert(now.end(),tt.begin(),tt.end());
    }
    return now;
}
int main(){
//  freopen("testdata.in","r",stdin);
    scanf("%d",&n),init();
    fp(i,1,n)scanf("%d",&a[i]),upd(res,calc(a[i],a[i]));
    for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),Add(u,v),Add(v,u);
    dfs(1,0);
    printf("%d\n",res);
    return 0;
}

\(F\)

先考虑一个暴力,把\(S\)中每一个数的倍数都存在一个\(bitset\)里,记为\(A\),那么答案就是\(A\&(A>>1)\&(A>>2)\)

然而这样显然会\(T\),那么我们考虑设阈值,对于\(x\)大于阈值的部分,我们还是直接枚举倍数并记到\(A\)里,否则我们找出\(x\)的倍数的循环节的情况,然后在循环节上对\(A\)处理就行了

顺便记得找循环节的时候千万别取模……\(\%\)的常数非常大甚至会\(T\)……

//quming
#include<bits/stdc++.h>
#define R register
#define pc(x) __builtin_popcountll(x)
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef unsigned long long ll;
const int N=(1000000000>>6)+233;
struct Bit{
    ll p[N];
    inline void ins(R int x){p[x>>6]|=(1ull<<(x&63));}
}A;
ll b[64*64+5];int n,S,mx,res;
int main(){
//  freopen("testdata.in","r",stdin);
    scanf("%d%d",&n,&S),mx=(n>>6);
    for(R int x;S;--S){
        scanf("%d",&x);
        if(x==1)return printf("%d\n",n-2),0;
        if(x<=64){
            fp(i,0,x-1)b[i]=0;
            for(R int i=0;i<(x<<6);i+=x)b[i>>6]|=(1ull<<(i&63));
            for(R int i=0,j=0;i<=mx;++i){
                A.p[i]|=b[j];
                if(++j==x)j=0;
            }
        }else for(R int i=x;i<=n;i+=x)A.ins(i);
    }
    A.p[(n+1)>>6]&=(1ull<<((n+1)&63))-1;
    fp(i,0,mx)res+=pc(A.p[i]&(A.p[i]>>1)&(A.p[i]>>2));
    fp(i,1,mx){
        R ll tmp=(A.p[i-1]>>62)|((A.p[i]&3)<<2);
        res+=pc(tmp&(tmp>>1)&(tmp>>2));
    }
    printf("%d\n",res);
    return 0;
}

原文地址:https://www.cnblogs.com/yuanquming/p/11421087.html

时间: 2024-11-03 05:05:23

Comet OJ - Contest #9 & X Round 3题解的相关文章

Comet OJ - Contest #5

Comet OJ - Contest #5 总有一天,我会拿掉给\(dyj\)的小裙子的. A 显然 \(ans = min(cnt_1/3,cnt_4/2,cnt5)\) B 我们可以感性理解一下,最大的满足条件的\(x\)不会太大 因为当\(x\)越来越大时\(f(x)\)的增长速度比\(x\)的增长速度慢得多 其实可以证明,最大的满足的\(x\)不会超过\(100\) 因为没有任何一个三位数的各位之和大于等于\(50\) 所以我们就直接预处理\(1-99\)所有的合法的 暴力枚举即可 其实

符文能量(Comet OJ - Contest #8)

给Comet OJ打个小广告,挺好用的,比较简洁,给人感觉很好用 Contest #8是我打的第一场本oj比赛,很遗憾A了前两道傻逼题就没思路了,然后就不打算打了....... https://www.cometoj.com/contest/58/problem/C?problem_id=2760 怎么做啊完全不会啊我那么菜,虽然看到是dp但嫌太麻烦就放弃了: 靠后仔细想了想原来这道题很简单: 结构体node e[];储存ai,bi值(当然你用数组我也不拦着),因为合并的方式很特殊,可以不管合并

Comet OJ - Contest #10 B

Comet OJ - Contest #10 B 沉鱼落雁 思维题 题意 : 每个数字最多重复出现三次,有n给数字,让你尽可能的使得相同数字之间的最小距离尽可能大 思路 :分三种情况套路 设 a b c 分别代表出现 一次, 两次, 三次 数字的个数 所有元素至多出现一次,答案为 n,题目规定 所有元素至多出现两次, 例如 1 1 2,可以排列成 1 2 1,所以,答案为 1 例如 1 1 2 2 3,可以排列成 1 2 3 1 2,所有 答案为 2 思考后得出,应该尽可能的把 b 个出现两次的

Comet OJ - Contest #15题解

A 双十一特惠 (简单版) n  <=  1e19,   1e9 > 1(8) https://www.cometoj.com/contest/79/problem/A?problem_id=4198 #include<bits/stdc++.h> using namespace std; int main(){ int t; cin >> t; while(t--) { int cnt1 = 0; int n;cin >> n; int pr[] = {1

Comet OJ - Contest #8题解

传送门 \(A\) 咕咕咕 const int N=1005; char s[N][N];int len[N],n,id; inline bool cmp(R int j,R int k){ R int l=min(len[j],len[k]); fp(i,1,l)if(s[j][i]!=s[k][i])return s[j][i]<s[k][i]; return len[j]<len[k]; } int main(){ scanf("%d",&n),id=1; f

Comet OJ - Contest #14题解

Contest14的本质:区间覆盖+Tarjan( A 把距离公式两边平方即可 注意要long long code #include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #define fo(a,b,c) for (a=b; a<=c; a++) #define fd(a,b,c) for (a=b;

Comet OJ - Contest #13 补题题解

A.险恶的迷宫 题意:在二维平面坐标内,给出一个圆心坐标 (a,b),以及圆的半径 r , 再给出 n 个点的坐标 (x_i,y_i),  求有多少点在圆内. 数据范围:0  <  n  <= 1e5,      0< r , x , y  <=1e9 思路:对于判断距离根据勾股定理: sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) <= r ,即在圆的范围内.由于此题数据较大,sqrt可能导致精度损失,所以直接开long long 进行平方比较

Comet OJ - Contest #15

https://cometoj.com/contest/79/problem/D?problem_id=4219 题目描述 ※ 简单版与困难版的唯一区别是粗体字部份和 $v$ 的数据范围. 在双 11 时,心慧精品店有个特别的折价活动如下: 首先,我们定义一个正整数为"好的"当且仅当此数仅由数字 1 构成,举例来说 1, 11, 111, 11111 都是「好的」,但 10.123.321 都是「不好的」. 接着,若一个商品原价为 x,若顾客能把 x 表示为 k 个「好的」数字,那么此

[Comet OJ - Contest #4 D][39D 1584]求和_&quot;数位dp&quot;

求和 题目大意: 数据范围: 题解: 脑筋急转弯可还行..... 我们发现只需要最后枚举个位/xk/xk 因为前面的贡献都是确定的了. 故此我们最后暴力统计一下就好咯. 不知道为啥我组合数一直过不去,暴力求过了呜呜. 代码: #include <bits/stdc++.h> using namespace std; typedef long long ll; char *p1, *p2, buf[100000]; #define nc() (p1 == p2 && (p2 =