2024.11.16
今日总结:
上午打了南外的比赛,下午改题 + 学习了树上差分并复习了lca,晚上写了图论专题,和树上的典型题目
1:打比赛
这道题是一道按位运算的模拟题目,只需要模拟出来闰年的个数即可 ,主要考察按位运算的应用
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 410;
int T;
int ryear[N],Friday[N],over[N];
void is_rn()
{
for(int i = 1;i < N;i ++)
{
if(i % 400 == 0) ryear[i] = 1;
else if(i % 100 == 0) ryear[i]=0;
else if(i % 4 == 0) ryear[i] = 1;
else ryear[i] = 0;
}
}
void Init()
{
is_rn();
Friday[1] = 2;
for(int i = 2;i < N;i ++)
{
if(ryear[i - 1])
{
Friday[i] = Friday[i - 1] - 2;
if(Friday[i] <= 0) Friday[i] = (Friday[i] + 7) % 8;
}
else
{
Friday[i] = Friday[i - 1] - 1;
if(Friday[i] <= 0) Friday[i] = (Friday[i] + 7) % 8;
}
}
for(int i = 2;i < N;i ++)
{
if(Friday[i] == 7) over[i]=1;
if(Friday[i] == 6) if(!ryear[i]) over[i]=1;
}
for(int i = 1;i < N;i ++)
over[i] = over[i - 1] + over[i];
}
signed main()
{
freopen("date.in","r",stdin);
freopen("date.out","w",stdout);
Init();
int average = over[400] - over[0];
scanf("%d",&T);
while(T --)
{
int m1,m2,y1,y2;
scanf("%lld%lld%lld%lld",&m1,&y1,&m2,&y2);
if(m1 >= 3) y1 ++;
if(m2 <= 1) y2 --;
int r = over[y2 % 400] + ((y2 / 400) * average);
int l = over[(y1 - 1) % 400] + (((y1 - 1) / 400) * average);
printf("%lld\n",r - l);
}
return 0;
}
2:医生厨
这道题是一道贪心的题目,主要是理解题目的意思,找规律即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T,n,x,ans;
int a[N];
int main()
{
freopen("drchef.in","r",stdin);
freopen("drchef.out","w",stdout);
scanf("%d",&T);
while(T --)
{
ans = 0;
scanf("%d%d",&n,&x);
for(int i = 1;i <= n;i ++)
scanf("%d",&a[i]);
sort(a + 1,a + 1 + n);
for(int i = 1;i <= n;i ++)
{
if(a[i] * 2 < x) ans ++;
else
{
while(1)
{
if(a[i] <= x) break;
x += x;
ans ++;
}
ans ++;
x = a[i] * 2;
}
}
printf("%d\n",ans);
}
return 0;
}
3:次小生成树
这道题是一道典型的树上差分 + 最小生成树的题目 这道题是主要使用lca倍增 + kruskal来解决
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010, M = 300010, INF = 0x3f3f3f3f;
int n, m;
struct Edge
{
int a, b, w;
bool used;
bool operator< (const Edge &t) const
{
return w < t.w;
}
}edge[M];
int p[N];
int h[N], e[M], w[M], ne[M], idx;
int depth[N], fa[N][17], d1[N][17], d2[N][17];
int q[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
LL kruskal()
{
for (int i = 1; i <= n; i ++ ) p[i] = i;
sort(edge, edge + m);
LL res = 0;
for (int i = 0; i < m; i ++ )
{
int a = find(edge[i].a), b = find(edge[i].b), w = edge[i].w;
if (a != b)
{
p[a] = b;
res += w;
edge[i].used = true;
}
}
return res;
}
void build()
{
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ )
if (edge[i].used)
{
int a = edge[i].a, b = edge[i].b, w = edge[i].w;
add(a, b, w), add(b, a, w);
}
}
void bfs()
{
memset(depth, 0x3f, sizeof depth);
depth[0] = 0, depth[1] = 1;
q[0] = 1;
int hh = 0, tt = 0;
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (depth[j] > depth[t] + 1)
{
depth[j] = depth[t] + 1;
q[ ++ tt] = j;
fa[j][0] = t;
d1[j][0] = w[i], d2[j][0] = -INF;
for (int k = 1; k <= 16; k ++ )
{
int anc = fa[j][k - 1];
fa[j][k] = fa[anc][k - 1];
int distance[4] = {d1[j][k - 1], d2[j][k - 1], d1[anc][k - 1], d2[anc][k - 1]};
d1[j][k] = d2[j][k] = -INF;
for (int u = 0; u < 4; u ++ )
{
int d = distance[u];
if (d > d1[j][k]) d2[j][k] = d1[j][k], d1[j][k] = d;
else if (d != d1[j][k] && d > d2[j][k]) d2[j][k] = d;
}
}
}
}
}
}
int lca(int a, int b, int w)
{
static int distance[N * 2];
int cnt = 0;
if (depth[a] < depth[b]) swap(a, b);
for (int k = 16; k >= 0; k -- )
if (depth[fa[a][k]] >= depth[b])
{
distance[cnt ++ ] = d1[a][k];
distance[cnt ++ ] = d2[a][k];
a = fa[a][k];
}
if (a != b)
{
for (int k = 16; k >= 0; k -- )
if (fa[a][k] != fa[b][k])
{
distance[cnt ++ ] = d1[a][k];
distance[cnt ++ ] = d2[a][k];
distance[cnt ++ ] = d1[b][k];
distance[cnt ++ ] = d2[b][k];
a = fa[a][k], b = fa[b][k];
}
distance[cnt ++ ] = d1[a][0];
distance[cnt ++ ] = d1[b][0];
}
int dist1 = -INF, dist2 = -INF;
for (int i = 0; i < cnt; i ++ )
{
int d = distance[i];
if (d > dist1) dist2 = dist1, dist1 = d;
else if (d != dist1 && d > dist2) dist2 = d;
}
if (w > dist1) return w - dist1;
if (w > dist2) return w - dist2;
return INF;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
edge[i] = {a, b, c};
}
LL sum = kruskal();
build();
bfs();
LL res = 1e18;
for (int i = 0; i < m; i ++ )
if (!edge[i].used)
{
int a = edge[i].a, b = edge[i].b, w = edge[i].w;
res = min(res, sum + lca(a, b, w));
}
printf("%lld\n", res);
return 0;
}
4:距离
这道主要是考察tarjanl离线 + lca的运用 需要将两点路径的距离转化为树的的祖宗节点到两点的距离减去二倍的到两点最近公共祖先的距离就是最终答案
当然也可以直接用lca解决,但主要练习的是离线tarjan + lca
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e4 + 10;
const int M = 2e4 + 10;
vector<PII> vq[N];
int n,m;
int res[M],st[N],f[N],dis[N];
int head[N],nxt[M],to[M],w[M],idx;
void Add(int a,int b,int c)
{
idx ++;
to[idx] = b;
w[idx] = c;
nxt[idx] = head[a];
head[a] = idx;
}
void dfs(int u,int fa) // 寻找根节点与每个点的距离
{
for(int i = head[u];i;i = nxt[i])
{
int j = to[i];
if(j == fa) continue;
dis[j] = dis[u] + w[i];
dfs(j,u);
}
}
int Find(int x)
{
if(f[x] != x) f[x] = Find(f[x]);
return f[x];
}
void tarjan(int u)
{
st[u] = 1;
for(int i = head[u];i;i = nxt[i])
{
int j = to[i];
if(!st[j])
{
tarjan(j);
f[j] = u;
}
}
for(auto it : vq[u])
{
int y = it.first,id = it.second;
if(st[y] == 2)
{
int hebing = Find(y);
res[id] = dis[u] + dis[y] - 2 * dis[hebing];
}
}
st[u] = 2;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n - 1;i ++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
Add(a,b,c),Add(b,a,c);
}
for(int i = 1;i <= m;i ++)
{
int a,b;
scanf("%d%d",&a,&b);
if(a != b)
{
vq[a].push_back({b,i});
vq[b].push_back({a,i});
}
}
for(int i = 1;i <= n;i ++)
f[i] = i;
dfs(1,-1);
tarjan(1);
for(int i = 1;i <= m;i ++)
printf("%d\n",res[i]);
return 0;
}
/*
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
const int M = 2e4 + 10;
int n,m,final_ans;
int dep[N],dis[N],fa[N][16];
int head[N],nxt[M],to[M],w[M],idx;
queue<int> q;
void Add(int a,int b,int c)
{
idx ++;
w[idx] = c;
nxt[idx] = head[a];
to[idx] = b;
head[a] = idx;
}
void bfs(int root)
{
memset(dep,0x3f,sizeof(dep));
q.push(root);
dep[0] = 0,dep[1] = 1;
while(!q.empty())
{
int t = q.front();
q.pop();
for(int i = head[t];i;i = nxt[i])
{
int j = to[i];
if(dep[j] > dep[t] + 1)
{
dep[j] = dep[t] + 1;
dis[j] = dis[t] + w[i];
q.push(j);
fa[j][0] = t;
for(int k = 1;k <= 14;k ++)
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
int lca(int a,int b)
{
if(dep[a] < dep[b]) swap(a,b);
for(int k = 14;k >= 0;k --)
if(dep[fa[a][k]] >= dep[b]) a = fa[a][k];
if(a == b) return a;
for(int k = 14;k >= 0;k --)
{
if(fa[a][k] != fa[b][k])
{
a = fa[a][k];
b = fa[b][k];
}
}
return fa[a][0];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n - 1;i ++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
Add(a,b,c),Add(b,a,c);
}
bfs(1);
int ans;
for(int i = 1;i <= m;i ++)
{
int a,b;
scanf("%d%d",&a,&b);
int p = lca(a,b);
ans = dis[a] + dis[b] - dis[p] * 2;
printf("%d\n",ans);
}
return 0;
}*/
5:通信线路
这道题是一道最短路 + 二分的题目,这题的难点在于并不能看出要求最短路,需要二分答案,二分答案的性质可以转换成求最短路的问题
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1010,M = 2e4 + 10;
int n,m,k;
int dis[N];
int head[N],nxt[M],to[M],w[M],idx;
deque<int> dq;
bool st[N];
void Add(int a,int b,int c)
{
idx ++;
to[idx] = b;
w[idx] = c;
nxt[idx] = head[a];
head[a] = idx;
}
bool check(int bound)
{
memset(dis,0x3f,sizeof(dis));
memset(st,0,sizeof(st));
dq.push_back(1);
dis[1] = 0;
while(!dq.empty())
{
int t = dq.front();
dq.pop_front();
if(st[t]) continue;
st[t] = true;
for(int i = head[t];i;i = nxt[i])
{
int j = to[i],x = w[i] > bound;
if(dis[j] > dis[t] + x)
{
dis[j] = dis[t] + x;
if(!x) dq.push_front(j);
else dq.push_back(j);
}
}
}
return dis[n] <= k;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
while(m --)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
Add(a,b,c),Add(b,a,c);
}
int l = 0,r = 1e6 + 1;
while(l < r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
if(r == 1e6 + 1) printf("-1\n");
else printf("%d\n",r);
return 0;
}
6:Building Roads S
读题可以看出这道题的要求其实是想让我们求出一个最小生成树,这点很难理解到,剩下就是一道kruskal的模板题,当然最重要的是关注距离的精度问题,否则会WA
点击查看代码
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5000100;
int n, m, idx, fa[N], sum;
double ans;
struct Node {
int x, y;
}g[N];
struct node {
int from, to;
double w;
}edge[N];
int read() {
int s = 0, w = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') w = -1;ch = getchar();}
while(isdigit(ch)) {s = s * 10 + ch - '0';ch = getchar();}
return s * w;
}
void add(int x, int y, double z) {
edge[++idx].from =x;
edge[idx].to = y;
edge[idx].w = z;
}
double jl(int x, int y) {
return (double)(sqrt((double)(g[x].x - g[y].x) * (g[x].x - g[y].x) + (double)(g[x].y - g[y].y) * (g[x].y - g[y].y)));
}
bool cmp(node x, node y) {
if(x.w == y.w) return x.from < y.from;
return x.w < y.w;
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int main() {
n = read(), m = read();
for(int i = 1; i <= n; i++)
g[i].x = read(), g[i].y = read();
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
double z = jl(i, j);
add(i, j, z);
}
}
for(int i = 1; i <= m; i++) {
int x = read(), y = read();
add(x, y, 0.0);
}
sort(edge + 1, edge + 1 + idx, cmp);
for(int i = 1; i <= idx; i++) {
int dx = find(edge[i].from), dy = find(edge[i].to);
if(dx != dy) {
fa[dx] = dy;
sum++;
ans += edge[i].w;
}
if(sum == n - 1) break;
}
printf("%.2lf\n", ans);
return 0;
}