已见过知识点:
链式前向星,floyed,dij,spfa,prim,kruscal,拓扑排序,强连通分量,差分约束,johnson全源最短路,次短路,二分图最大匹配、最小点覆盖,割点和桥,最大流,lca,rmq,st表,树上倍增,kruscal重构树
见过的技巧:
反向建图,黑白染色,点权转边权,加平台点,权值01化,边化点(kruscal重构树),反向bfs,拆点
踩过的坑:
nmd,给我仔细看好每个数组应该开多大!!!
写lca的时候数组尽量多开一点,不然会寄,而且开得多跑得还快了(
写return 0;
init写最外面
别写endl,这玩意慢得要死,以后都#define endl '\n'
待补知识点:
树链剖分,树分治,k短路,双连通,圆方树,最小费用最大流
题目列表:
[P2419 USACO08JAN]Cow Contest S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P6154 游走 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
[P1948 USACO08JAN]Telephone Lines S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P1491 集合位置 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
https://ac.nowcoder.com/acm/problem/51269
[P2419 USACO08JAN]Cow Contest S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
虽然只是一个黄题,但是我没见过这种做法,所以记一下(我本来是在刷拓扑排序,不知道怎么突然冒出来一个这个题)
题意:一张代表能力强弱关系的有向图,问最多能确定多少人的排名。
思路:我本来写的反向建图拓扑排序,发现不对,因为找不到什么好的结论去推答案,然后翻了翻题解,就没人写拓扑排序,用的floyed判任意两点之间的连通性,想想也是,毕竟N才100。
#include <bits/stdc++.h>
using namespace std;
int mp[101][101];
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
mp[a][b]=1;
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
mp[i][j]|=(mp[i][k]&&mp[k][j]);
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
int tes=1;
for(int j=1;j<=n;j++){
if(i==j){
continue;
}
if((mp[i][j]|mp[j][i])==0){
tes=0;
break;
}
}
if(tes==1){
ans++;
}
}
cout<<ans<<endl;
return 0;
}
P6154 游走 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
拓扑排序+dp+期望,看完题解觉得不过如此,md怎么就没做出来呢?
题意:一张有向无环图,计算所有路径的长度期望
思路:我本来想的是记录下到达每个点分别有哪些长度的点,每个长度对应几条边,但是t了,因为我没有必要知道具体的每条边的长度,我需要知道的是总长度和总路径数,因为都是等概率的,所以其实只要记录下到每个点路径总长度和总条数即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int mod=998244353;
ll quick_pow(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll inv(ll x) {return quick_pow(x, mod-2);}
void read(int &x){
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
struct node{
int ne,to,val;
};
node edge[700001];
int head[100001];
int cnt=0;
void addedge(int a,int b){
edge[cnt].ne=head[a];
edge[cnt].to=b;
head[a]=cnt++;
}
int ru[100001];
int num[100001];
int dis[100001];
int n,m;
int sum=0;
int cn=0;
void tuopu(){
queue<int>q;
for(int i=1;i<=n;i++){
if(ru[i]==0){
q.push(i);
}
num[i]=1;
}
while(!q.empty()){
int t=q.front();
q.pop();
//cout<<t<<endl;
for(int i=head[t];i!=-1;i=edge[i].ne){
int son=edge[i].to;
ru[son]--;
num[son]+=num[t];
num[son]%=mod;
//这个地方想了一会:因为dis[t]存的是到t的总路径长度,想要从t走到son,每一条以t为终点的路径都需要经过edge[i]这条边,从而导致长度+1,因此这里要加上num[t]
dis[son]+=dis[t]+num[t];
dis[son]%=mod;
if(ru[son]==0){
q.push(son);
}
}
}
for(int i=1;i<=n;i++){
sum+=dis[i];
sum%=mod;
cn+=num[i];
cn%=mod;
}
}
signed main(){
memset(head,-1,sizeof(head));
read(n);
read(m);
for(int i=0;i<m;i++){
int a,b;
read(a);
read(b);
addedge(a,b);
ru[b]++;
}
tuopu();
//cout<<sum<<" "<<cn<<endl;
cout<<sum*inv(cn)%mod<<endl;
return 0;
}
[P1948 USACO08JAN]Telephone Lines S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:求原点1到n的所有路中的第k+1长的路最小值。
思路:二分应该不难想到,我们去二分这个第k+1长的路的长度,但关键是如何根据这个长度去计算是否可行&#