2020牛客多校第四场By Rynar

A.Ancient Distance

题意:

思路:利用对分的方法求出所有ancient distance所需的key vertice数,利用key vertice不同的区间(ans[i-1]-ans[i]!=0)表示key vertice为一值时的最小距离

const int N=3e6+100;
int n;
vector<int>v[N];
int dep[N],dfn[N],fa[N],cnt;
void dfs(int x,int f){
    dfn[++cnt]=x;
    fa[x]=f;
    dep[x]=dep[f]+1;
    for (int i:v[x]){
        if (i==f)continue;
        dfs(i,x);
    }
}
int d[N];
int find(int x){//找到ancient distance为x所需的key vertice数
    int sum=0;
    for(int i=1;i<=n;++i)d[i]=dep[i];
    for(int i=n;i>=1;i--){
        int t=dfn[i];
        if(i==1||d[t]-dep[t]==x){
            sum++;
            d[t]=-1;
        }
        d[fa[t]]=max(d[fa[t]],d[t]);
    }
    return sum;
}
int ans[N];//记录ancient distance所需的key vertice数
void merge(int x,int y,int l,int r){
    if(x>y||l>r)return;
    if(l==r){
        for(int i=x;i<=y;i++) ans[i]=l;
        return;
    }
    int mid=(x+y)>>1;
    ans[mid]=find(mid);
    merge(x,mid-1,ans[mid],r);
    merge(mid+1,y,l,ans[mid]);
}
int main() {
    int x;
    while (cin>>n){
        cnt=0;
        for (int i=1;i<=n;i++)v[i].clear();
        for (int i=1;i<n;i++){
            scanf("%d",&x);
            v[x].push_back(i+1);
            v[i+1].push_back(x);
        }
        dep[0]=-1;dfs(1,0);
        int mx=0;
        for (int i=1;i<=n;i++)mx=max(mx,dep[i]);
        merge(0,mx,1,n);
        ll sum=0;
        for (int i=1;i<=mx;i++)sum+=1ll*i*(ans[i-1]-ans[i]);
        printf("%lld\n",sum);
    }
    return 0;
}

B.Basic Gcd Problem

C.Count New String

题意:
思路:把每个字符串的后缀以后缀为空状态插入后缀自动机,用stack记录重新换点的状态,例dbca中的bcc->dddd先比较d和bcc一直到空状态重新插入

const int N=3e6+100;
typedef long long ll;
struct SAM{
    int ch[N*2][26],len[N*2],fa[N*2];
    int tot=1,last=1;
    int endpos_size[N];//
    inline int add(int x,int last){
        if(ch[last][x]) {
            int p=last,t=ch[p][x];
            if(len[p]+1==len[t]) return t;
            else{
                int y=++tot;
                len[y]=len[p]+1;
                memcpy(ch[y],ch[t],sizeof(ch[t]));
                while(p&&ch[p][x]==t) ch[p][x]=y,p=fa[p];
                fa[y]=fa[t];fa[t]=y;
                return y;
            }
        }
        int p=last,np=++tot;
        len[np]=len[p]+1; endpos_size[np]=1;//
        for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
        if(!p) fa[np]=1;
        else {
            int q=ch[p][x];
            if(len[q]==len[p]+1) fa[np]=q;
            else {
                int nq=++tot;len[nq]=len[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
            }
        }
        return np;
    }
    inline void getsum(){
        ll ans=0;
        for(register int i=2;i<=tot;++i)ans+=len[i]-len[fa[i]];
        printf("%lld\n",ans);
    }
}sam;
int n;
char s[N];
stack<pair<int,int> >p;//当前位置点和它的状态值last
int main() {
    scanf("%s",s);
    int len=strlen(s),last=1;
    for (int i=len-1;i>=0;i--){
        int x=s[i]-'a';
        int del=0;
        while (!p.empty()&&p.top().first<x)del++,p.pop();
        if (p.empty())last=1;
        else last=p.top().second;
        del++;
        while (del--){
            last=sam.add(x,last);
            p.push({x,last});
        }
    }
    sam.getsum();
    return 0;
}

D.Dividing Strings

E.Eliminate++

F.Finding the Order

G.Geometry Challenge

H.Harder Gcd Problem

题意:1-n中找两个数a,b,使得gcd(a,b)!=1,问这样的每对数都完全不同的数对可以组成多少对
思路:先用素数筛找出所有素数,从大到小,列出prim[n]到n/prim[n]*prim[n]的所有prim[i]的倍数,先把头和尾没有组过的数组成一对,再以大到小两两一对,若剩下一个数,不用管他,因为之后必然存在一个小于该质数的质数会遍历到它。另外可知,数对对数应为(n-1-(n/2+1到n的素数总数))/2

const int N=2e5+10;
int n,m,k;
int a[N],b[N];
int vis[N],prim[N],is[N];
int num=0;
void euler(int n){
    num=0;
    for (int i=2;i<=n;i++){
        if (vis[i]==0)prim[++num]=i;
        for (int j=1;j<=num&&prim[j]*i<=n;j++){
            vis[i*prim[j]]=1;
            if (i%prim[j]==0)break;
        }
    }
}
vector<pair<int,int>>p;
void solve(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++)is[i]=0;
    int t=upper_bound(prim+1,prim+1+num,n)-prim-1;
    int cnt=0;
    for (int i=t;i>=1;i--){
    	int f=0,ff=0;
    	for (int j=n/prim[i];j>=2;j--){
            if (!is[j*prim[i]]){
    	        if (ff==0){
    		    ff=1;
    		    cnt++;
    		    p.push_back({j*prim[i],prim[i]});
                    is[j*prim[i]]=1;is[prim[i]]=1;
                }
    		else if (f==0)f=j;
    		else{
    		    cnt++; 
    		    p.push_back({f*prim[i],j*prim[i]});
                    is[f*prim[i]]=1;is[j*prim[i]]=1;
    	            f=0;
		}
	    }
	}
    }
    printf("%d\n",cnt);
    for (int i=0;i<cnt;i++){
	printf("%d %d\n",p[i].first,p[i].second);
    }
    p.clear();
}
int main(){
    euler(2e5);
    int T=1;
    scanf("%d",&T);
    while (T--){
        solve();
    }
    return 0;
}

I.Investigating Legions

J.Jumping on the Graph

posted @ 2020-07-20 17:55  Rynar  阅读(231)  评论(0)    收藏  举报