Codeforces Round #845 (Div. 2) and ByteRace 2023 C-E
碎碎念
这次a和b过的可能是有生以来最快的(?)然后写C写了一个多小时还没写出来(悲)最后剩不到二十分钟看了眼D,结果瞬间悟出了答案(属于是开题失误了)最后以最快速度写完,一抬头发现刚好时间到了(樂)属于是到手的紫名飞了呜呜
C题
题意
思路
比赛时没有注意到的一个重要性质是答案只与最大值和最小值相关,这是十分不应该的。注意到这一点后对所有smartness排序就成为了很自然的想法。接下来就是考虑对于左端点为l时维护当右端点r取到哪里时能凑出的topic的数目为m了,这可以利用双指针线性求出。
D题
题意
大概就是说有个树,每个节点都可能是1或者是0,每过一秒所有节点的值都会变成它们儿子异或起来的值,叶子则会直接变成0,求所有可能的树的初始状态下每秒1棵树所有节点的和的和(有点绕)
思路
因为是考虑所有可能的状态(这种技巧最近十分常见),不妨从考虑概率的角度入手。这里有一个很有趣的point:
于是我们只需要算出每秒可能非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出奇的的简单啊,一眼二分翻转的最大边权,然后把所有小于的边变为双向边检测是否连通即可。有趣的是题解在求解连通性时使用的手法。
首先介绍一下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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端