Codeforces Round 911 (Div. 2)

比赛录屏

https://www.bilibili.com/video/BV1H34y1F7B1/

\(A. Cover in Water\)

https://codeforces.com/contest/1900/submission/234430348

\(B. Laura and Operations\)

https://codeforces.com/contest/1900/submission/234447465

\(C. Anji's Binary Tree\)

https://codeforces.com/contest/1900/submission/234455131

\(D. Small GCD\)

题意:定义 \(f(x,y,z)\) 为其中较小的两数字的 \(gcd\) ,求一个数组的 \(\sum _{i=1}^{n}\sum _{j=i+1}^{n}\sum _{k=j+1}^{n} f(a_i,a_j,a_k)\)
解法:赛场很容易观察到:答案与数组的顺序无关,所以我们从小到大排序容易计算任两对的状态。然后就陷入僵局了,我并不知道如何处理 \(gcd\)
对于两个数字的 \(gcd\) ,我们可以把 \(gcd\) 的贡献分给较大数字的约数。例如 \(16\)\(8\)\(gcd\)\(8\) ,我们可以看作是 \(16\) 的约数 \(1,2,4,8,16\) 的和贡献。其中 \(1\) 的贡献为 \(1\)\(2\) 的贡献为 \(2-1\)\(4\) 的贡献为 \(4-1-1=2\)\(8\) 的贡献为 \(8-1-1-2-=4\) ,以此类推。我们可以发现我们本质其实是把 \(gcd\) 转化为对每个数的所有约数做一个前缀和,那么约数的约数要被去重。这样计算就是一个 \(O(\sum(a_i的约数个数))\) 的复杂度,由于 \(a_i\le 1e5\) ,并不大,所以复杂度并不高。

vector<int>divs[N];
int phi[N],a[N];
void solve(){
	int n=read(),ans=0;
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	sort(a+1,a+1+n);
	vector<int>cnt(N);
	for(int i=1;i<=n;i++){
		for(auto x:divs[a[i]]){
			ans+=phi[x]*cnt[x]*(n-i);	
			cnt[x]++;	
		}
	}
	cout<<ans<<'\n';
    //puts(ans>0?"YES":"NO");
    //puts(ans>0?"Yes":"No");
}
signed main(){
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    // cout.tie(0);
	for(int i=1;i<=100000;i++){	
		for(int j=i;j<=100000;j+=i){
			divs[j].push_back(i);
		}
	}
	for(int i=1;i<=100000;i++){
		phi[i]=i;
	}
	for(int i=1;i<=100000;i++){
		for(int j=2*i;j<=100000;j+=i){
			phi[j]-=phi[i];	//
		}
	}
   int t=read();
    while(t--){
        solve();
    }
}

\(E. Transitive Graph\)

题意:给定一个有向图,有点权,重复进行如下操作:如果存在边 \(a\rightarrow b\rightarrow c\) 则加边 \(a\rightarrow c\) 。询问点权之和最小且经过点数最多的简单路径的长度以及点权之和。
解法:首先,对于强联通分量,我们必然能找出一条路径经过所有点得到一条最长路。如果有一条边指向强联通时,我们可以利用题目的操作仍然遍历整个强联通分量。所以变了一个含若干强联通分量的拓扑图上的最长且点权最小的点,显然的 \(dp\) 可以完成这个操作。

int cnt,low[N],num[N],dfn,sccno[N],sta[N],top;
vector<int>G[N],tp[N];
void dfs(int u){
    sta[top++]=u;
    low[u]=num[u]=++dfn;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(!num[v]){
            dfs(v);
            low[u]=min(low[v],low[u]);
        }else if(!sccno[v]){
            low[u]=min(low[u],num[v]);
        }
    }
    if(low[u]==num[u]){
        cnt++;
        while(1){
            int v=sta[--top];
            sccno[v]=cnt;
            if(u==v)break;
        }
    }
}
void Tarjan(int n){
    cnt=top=dfn=0;
    for(int i=0;i<=n;i++){
        sccno[i]=num[i]=low[i]=0;
    }
    // memset(sccno,0,sizeof sccno);
    // memset(num,0,sizeof num);
    // memset(low,0,sizeof low);
    for(int i=1;i<=n;i++){
        if(!num[i]){
            dfs(i);
        }
    }
}
void solve(){
    int n=read(),m=read();
    vector<int>a(n+1);
    for(int i=1;i<=n;i++){
        G[i].clear();
        tp[i].clear();
        a[i]=read();
    }
    for(int i=1;i<=m;i++){
        G[read()].push_back(read());
    }
    Tarjan(n);
    vector<PII>info(cnt+1),f(cnt+1);
    for(int i=1;i<=n;i++){
        info[sccno[i]].first++;
        info[sccno[i]].second-=a[i];
        for(auto x:G[i]){
            if(sccno[i]!=sccno[x])tp[sccno[i]].push_back(sccno[x]);
        }
    }
    for(int i=cnt;i>=1;i--){
        f[i].first+=info[i].first;
        f[i].second+=info[i].second;
        for(auto x:tp[i]){
            f[x]=max(f[x],f[i]);
        }
    }
    auto ans=*max_element(f.begin(),f.end());
    cout<<ans.first<<" "<<-ans.second<<'\n';
    //puts(ans>0?"YES":"NO");
    //puts(ans>0?"Yes":"No");
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    // int t=1;
   int t=read();
    while(t--){
        solve();
    }
}
posted @ 2023-11-27 00:24  EdGrass  阅读(41)  评论(0)    收藏  举报