Codeforces Round #845 (Div. 2) and ByteRace 2023 C-E
  Nt5IRVXsz2kM 2023年11月01日 40 0

碎碎念

这次a和b过的可能是有生以来最快的(?)然后写C写了一个多小时还没写出来(悲)最后剩不到二十分钟看了眼D,结果瞬间悟出了答案(属于是开题失误了)最后以最快速度写完,一抬头发现刚好时间到了(樂)属于是到手的紫名飞了呜呜

C题

题意

image

思路

比赛时没有注意到的一个重要性质是答案只与最大值和最小值相关,这是十分不应该的。注意到这一点后对所有smartness排序就成为了很自然的想法。接下来就是考虑对于左端点为l时维护当右端点r取到哪里时能凑出的topic的数目为m了,这可以利用双指针线性求出。

D题

题意


大概就是说有个树,每个节点都可能是1或者是0,每过一秒所有节点的值都会变成它们儿子异或起来的值,叶子则会直接变成0,求所有可能的树的初始状态下每秒1棵树所有节点的和的和(有点绕)

思路

因为是考虑所有可能的状态(这种技巧最近十分常见),不妨从考虑概率的角度入手。这里有一个很有趣的point:

\[\left(\begin{array}{c}n \\1 \end{array}\right) + \left(\begin{array}{c}n \\3 \end{array}\right) + \cdots = \left(\begin{array}{c}n \\0 \end{array}\right) + \left(\begin{array}{c}n \\2 \end{array}\right) +\cdots = \frac{1}{2}\]

\[于是我们大胆猜想:对于所有可能非0的节点,在每秒钟它们得分的期望是\frac{1}{2} \]

于是我们只需要算出每秒可能非0的节点有多少即可.很自然地想到可以用topsort来解决。这里犯了个铸币操作:对无向图dfs再改成有向图来保证拓扑排序的正确性,但事实上即使是无向图只要给根节点1添加一个虚根就可以直接拓扑排序了(如果比赛时想到是不是就来得及交题了呜呜)

今天还学到了一个关于vector的小细节:resize和clear本质上并没有释放内存,清vector最好还是vec.erase(vec.begin(),vec.end())

代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<utility>
#include<string.h>
#include<ext/rope> 
#include<queue>
#include<stack>
using namespace std;
using namespace __gnu_cxx;

#define int  long long
#define dnt double
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
#define dow(i,j,k) for(i=(j);i>=(k);--i)
#define pr pair
#define mkp make_pair
#define fi first
#define se second
int gp() {int x;while((x=rand())<=0)srand(time(0));return x%1000;}
inline int read(int &x) {
   x=0;int ff=1;char ch=getchar();
   while (ch<'0'||ch>'9') {
   	if (ch=='-') ff=-1;ch=getchar();
   }
   while (ch>='0'&&ch<='9') {
   	x=x*10+ch-48;ch=getchar();
   }
   return x*ff;
}
void write(int x) {
 if (x > 9)write(x / 10);
 putchar((x % 10) + '0');
}
inline int max(int a,int b) {return a>b?a:b;}
inline int min(int a,int b) {return a>b?b:a;}
inline int exgcd(int a,int b,int&x,int&y) {
   if(b==0) {x=1,y=0 ;return a ;} 
   else {int r=exgcd(b,a%b,x,y);int t=x ;x=y ;y=t-a*y/b ;return r ;}
}

struct matrix {
   int n,m;
   int num[102][102];
   void pri() {
   	for(int i=1; i<=n; i++) {
   		for(int j=1; j<=m; j++) {
   			cout<<num[i][j]<<" ";
   		}
   		cout<<endl;
   	}
   }
   matrix operator*(matrix y) {
   	matrix x=*this;
   	matrix c;
   	c.n=x.n;
   	c.m=y.m;
   	memset(c.num,0,sizeof(c.num));
   	for(int i=1; i<=x.n; i++) {
   		for(int k=1; k<=x.m; k++){
   			if(x.num[i][k])
   			for(int j=1; j<=y.m; j++){
   			c.num[i][j]+=x.num[i][k]*y.num[k][j];
   			//	c.num[i][j]+=((x.num[i][k]%mod)*(y.num[k][j]%mod))%mod;
   			}
   		}
   	}
   	return c;
   }
   matrix operator ^ (int x) {
   	matrix c,xx;
   	xx=*this;
   	c.m=xx.m;
   	c.n=xx.n;
   	for(int i=1; i<=xx.n; i++)
   		for(int j=1; j<=xx.m; j++)
   			c.num[i][j]=(i==j);
   	for(; x; x>>=1) {
   		if(x&1)
   			c=c*xx;
   		xx=xx*xx;
   	}
   	return c;
   }
};int mod=1e9+7;
int fpow(int x,int y) { 
   int ans=1;int base=x;
   while(y) {
   	if(y&1){
   	ans=ans*base;
   	ans%=mod;	
   	}
   	y/=2;
   	base=base*base;
   	base%=mod;
   }
   return ans%mod;
}
const int maxn=500005;
int lim=maxn,tot=0;int prime[maxn];bool tof[maxn];
void shai() {
   for(int i=2; i<=lim; i++) {if(tof[i] == false) {prime[++tot] = i;}
   	for(int j=1; j<=tot && i * prime[j] <=lim; j++) {tof[i * prime[j]] = true;if(i % prime[j] == 0) break;}}
}
int head[maxn];
int id=0;
struct Edge {
   int w,v,next;
} p[maxn*5];
void build(int u,int v,int w) {
   p[++id].v=v;p[id].w=w;
   p[id].next=head[u];
   head[u]=id;return; 
}
int f[maxn],a[maxn];int n,m,t,k;
//加油 注意初始化
int ru[maxn];

int dis[maxn];
queue<int>q;vector<int>edg[200005];
void dfs(int noww,int fa){
   if(fa!=0){
   	build(noww,fa,0);
   	ru[fa]++;
   }
   for(auto &it:edg[noww]){
   	if(it==fa)
   	continue;
   	dfs(it,noww);
   }
}
signed main() {
   ios::sync_with_stdio(false),cin.tie(NULL);
   //freopen("data.in","r",stdin);
   //freopen("T1.out","w",stdout);
   cin>>t;
   while(t--){
    cin>>n;
    int x,y;
    rep(i,1,n-1){
    	cin>>x>>y;
    	edg[x].push_back(y);
    	edg[y].push_back(x);
    	//build(x,y,0);
    //	build(y,x,0);
    //	ru[x]++;ru[y]++;
    } 
    dfs(1,0);
    int maxx=0;
    for(int i=1;i<=n;i++){
    	if(ru[i]==0){
    		q.push(i);
    		dis[i]=1;
    	//	f[0]++;
   	 }
    }
    while(!q.empty()){
    	int u=q.front();
    	q.pop();
    	f[dis[u]]++;
    	maxx=max(maxx,dis[u]);
    	for(int i=head[u];i;i=p[i].next){
    		int v=p[i].v;
    		ru[v]--;
    		if(ru[v]==0){
    			dis[v]=max(dis[v],dis[u]+1);
    			q.push(v);
   		 }
   	 }
    }
//	 rep(i,1,maxx)
//	 cout<<f[i]<<endl;
    rep(i,1,maxx){
    	a[i]=a[i-1]+f[maxx-i+1];
    	a[i]%=mod;
   //	 cout<<a[i]<<endl;
    }
   int ll=fpow(2,n-1);
   //cout<<ll;
   int anss=0;
   rep(i,1,maxx){
   	//cout<<ll*a[i]<<" "<<i<<" "<<a[i]<<endl;
   	anss+=ll*a[i];
   	anss%=mod;
   }
   cout<<anss<<endl;
   id=0;
   rep(i,0,n){
   	dis[i]=0;
   	ru[i]=0;
   	a[i]=0;
   	f[i]=0;
   	head[i]=0;
   	edg[i].erase(edg[i].begin(),edg[i].end());
   }
}
   return 0;
}
/*



*/

E题

题意

给定带权的有向图,你可以翻转若干条边,花费为所有翻转边中最大的边权,求使得存在一个点,它能到达其他所有点的最小花费。

思路

这次的E出奇的的简单啊,一眼二分翻转的最大边权,然后把所有小于的边变为双向边检测是否连通即可。有趣的是题解在求解连通性时使用的手法。
image

image
首先介绍一下kosaraju算法(感觉是个挺冷门的知识啊):用于求有向图中的强连通分量。说白了就是先dfs一次,如果有点没有访问过就把它所在的链全访问一遍,记录顺序,然后把所有链自己按顺序存储,链与链之间的顺序按逆序存储,然后在反图上再跑一边进行染色,染到的点就是一个强连通分量。
而题解中的代码本质上是用DFS1找了个拓扑排序的开头,然后尝试从这里走到其他点以检测连通性。这之所以成立是因为拓扑排序的开头本质上是最不可能经由其他点到达的点,如果从它开始能访问到其他点那就找到了答案。反之如果不行,那说明这个图没有更有可能到达其他点的起点了。
本题的代码就不写了,下面是kosaraju的板子,摘自其他博客

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int N = 100010;
vector<int> G[N],rG[N];
vector<int> S;  // 存第一次dfs1()的结果,即标记点的先后顺序,优先级小的点先进
int vis[N];    // vis[i]标记第一次dfs1()点i是否访问过
int sccno[N];  // sccno[i]标记点i属于第几个强连通分量,同时记录dfs2()过程中点i是否访问过
int cnt;    //cnt表示强连通分量的个数
void dfs1(int u){
    if(vis[u])  return;
    vis[u] = 1;
    for(int i=0; i<G[u].size(); i++)
        dfs1(G[u][i]);
    S.push_back(u); //记录点的先后顺序,按照拓扑排序,优先级大的放在S的后面
}

void dfs2(int u){
    if(sccno[u])    return;
    sccno[u] = cnt;
    for(int i=0; i<rG[u].size(); i++)
        dfs2(rG[u][i]);
}
void Kosaraju(int n) {
    cnt = 0;
    S.clear();
    memset(vis,0,sizeof(vis));
    memset(sccno,0,sizeof(sccno));
    for(int i=1; i<=n; i++) //搜索所有点
        dfs1(i);
    for(int i=n-1; i>=0; i--){
        if(!sccno[S[i]]){
            cnt++;
            dfs2(S[i]);
        }
    }
}
int main() {
    int n,m,u,v;
    while(cin >> n >> m && (n || m)) {
        for(int i=0; i<n; i++) {
            G[i].clear();
            rG[i].clear();
        }
        for(int i=0; i<m; i++) {
            cin >> u >> v;
            G[u].push_back(v);  // 原图
            rG[v].push_back(u); // 反图
        }
        Kosaraju(n);
    }
    return 0;
}
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  jTMfQq5cr55P   2024年05月17日   44   0   0 算法与数据结构
  jTMfQq5cr55P   2024年05月17日   40   0   0 算法与数据结构
Nt5IRVXsz2kM