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
禁止类似码农教程的网站爬取,抄袭博客内容。
https://www.cnblogs.com/rair/

浙公网安备 33010602011771号