D
题意:
有n天,访客逗留d 天。
有 k 项 工作,有这项 工作开始的 时间和结束的时间
问 哪一天 不同的工作数最多,那一天最多。
长度为 d 的 滑动窗口,我们记录 每一个时间点 开始的工作数量,和结束的工作数量。 加上新 加入 的点 所开始的工作,减去 离开区间的点 结束的数量。
void solve()
{
int n, d, k;
cin >> n >> d >> k;
vector<int> a(n + 3);
vector<int> b(n + 3);
int x, y;
while (k--)
{
cin >> x >> y;
a[x]++;
b[y]++;
}
int mx, mn = 0;
for (int i = 1; i <= d; i++)
{
mx += a[i];
}
mn = mx;
int ans1, ans2;
ans1 = 1;
ans2 = 1;
int t = mx;
for (int i = 2; i <= n - d + 1; i++)
{
t -= b[i - 1];
t += a[i + d - 1];
if (t > mx)
{
mx = t;
ans2 = i;
}
if (t < mn)
{
mn = t;
ans1 = i;
}
}
cout << ans2 << " " << ans1 << "\n";
}
E
前半边那个 在一个点汇合的有点像 百度23年的一道题
添加链接描述
两个人a b 在两个点出发,到N。a走一步体力消耗为TE,b走一步体力消耗为TF ,如果两个人汇合,那么两个人走一步的体力消耗和为TE+TF-s.问两个人到N点的最小体力消耗。
思路就是枚举汇合的地点。我们需要处理三个最短路(因为边权是固定的,所以可以用bfs)一个是a 到每个点的最短路,b 到每个店的最短路,每个点到N的最短路(其实就是N到每个点的最短路)
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 1) + (x << 3) + ch - '0';
ch = getchar();
}
return x * f;
}
const int N = 4e4 + 5;
vector<int> e[N];
int a[N], b[N], c[N];
void solve()
{
int te, tf, s;
cin >> te >> tf >> s;
int t, f, n, m;
cin >> t >> f >> n >> m;
int u, v;
while (m--)
{
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
auto bfs = [&](int s, int *a) -> void
{
for (int i=1;i<=n;i++)
a[i]=2e9;
queue<int> q;
q.push(s);a[s]=0;
while (!q.empty())
{
int u = q.front();
q.pop();
for (auto v : e[u])
{
// 已经遍历 这个点了
if (a[v]!=2e9)
continue;
a[v] = a[u] + 1;
q.push(v);
}
}
};
bfs(t,a);
bfs(f,b);
bfs(n,c);
int mn=2e9;
// 枚举汇合的点
for (int i=1;i<=n;i++)
{
mn=min(mn,a[i]*te+b[i]*tf+c[i]*(te+tf-s));
}
if (mn==2e9)cout<<-1<<"\n";
else
cout<<mn<<"\n";
}
signed main()
{
std::cin.tie(nullptr)->sync_with_stdio(false);
int t;
t = 1;
// cin>>t;
while (t--)
{
solve();
}
return 0;
}
这道题 主题 的思路 和这个一样。只不过在上一道题的基础上 多了分层。
我们 可以将 所以节点 没有马的情况 看做一层图,将所有 节点 有马的情况看做一层图。
然后 对于 有马的节点
考虑拆点。我们将点 拆成点 i 和点 n+i,其中点 i 表示到i 点时没有骑马,点 i+n表示到 点时骑上了马。那么对于一个有马的点 连边 (i ,i+n ,0),对于一条边 (u , v ,w) 连边 以及 (u+n ,v+n , w/2)。然后分别 跑 Dijkstra 求单源最短路。最后枚举两人到哪个点集合,并分别枚举两人到达时是否骑马即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e5 + 100;
vector<pair<int, int>> e[N];
struct node
{
int x;
int y;
bool operator<(const node &a) const
{
return a.y < y;
}
};
void solve()
{
int n, m, h;
cin >> n >> m >> h;
for (int i = 0; i <= 2 * n; i++)
{
e[i].clear();
}
for (int i = 0, u; i < h; i++)
{
cin >> u;
e[u].push_back({n+u,0});
}
for (int i = 0, u, v, w; i < m; i++)
{
cin >> u >> v >> w;
e[u].push_back({v, w});
e[v].push_back({u, w});
e[u + n].push_back({v + n, w / 2});
e[v + n].push_back({u + n, w / 2});
}
auto dij = [&](int s, vector<int> &dis) -> void
{
vector<bool> vis(2 * (n + 1), false);
dis[s] = 0;
priority_queue<node> q;
q.push({s, 0});
while (!q.empty())
{
node t = q.top();
q.pop();
int u = t.x;
if (vis[u])
continue;
vis[u] = 1;
for (auto [v, w] : e[u])
{
if (dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
q.push({v, dis[v]});
}
}
}
};
vector<int> dis1(2 * (n + 1), 1e18);
vector<int> dis2(2 * (n + 1), 1e18);
dij(1, dis1);
dij(n, dis2);
int ans = 1e18;
for (int i = 1; i <= n; i++)
{
ans = min(ans, max(dis1[i], dis2[i]));
ans = min(ans, max(dis1[i + n], dis2[i]));
ans = min(ans, max(dis1[i], dis2[i + n]));
ans = min(ans, max(dis1[i + n], dis2[i + n]));
}
if (ans == 1e18)
cout << -1 << "\n";
else
cout << ans << "\n";
}
signed main()
{
int t=1;
cin>>t;
while(t--)
{
solve();
}
}
F
题意:
一棵树,每次可以使 这个节点 的相邻节点都减c 使``得 将这个节点的价值 统计进答案。
求整棵树的最大价值和
显然的是可以转移的东西。通常树上的dp【i】代表 以i 为根节点 的子树 的状态。
// dp[i][1]代表 以i 为根的子树 ,根节点不取的情况下 的价值的最大值
// dp[i][0]代表 以i 为根的子树 ,根节点取的情况下 的价值的最大值
考虑一下转移
我们只需要 关注 那些选的节点的权值
dp[i] 的状态 是由 他的子节点 转移 过来的。
如果这个点统计进答案,那么和这个点相邻的点都要减去c
因为是相邻的节点 所以 转移的时候 要考虑 根节点对子节点的影响 子节点对根节点的影响
如果根节点不选的话,根节点不会对子节点造成减c的影响,子节点(不论子节点选不选) 无法 对根节点造成减c 的影响(因为 根节点的 价值不计入答案)
如果根节点选的话 ,从不选的子节点转移过来的时候,不造成影响(因为我们只关注选的节点),从选的子节点转移时,子节点对根节点 有 -c,根节点对子节点有-c ,所以要减去两个c
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 1) + (x << 3) + ch - '0';
ch = getchar();
}
return x * f;
}
const int N=2e5+5;
int dp[N][2];
// dp[i][1]代表 以i 为根的子树 ,根节点不取的情况下 的价值的最大值
// dp[i][0]代表 以i 为根的子树 ,根节点取的情况下 的价值的最大值
vector<vector<int>>e(N);
void solve()
{
memset(dp,sizeof(dp),0);
int n,c;cin>>n>>c;
vector<int>a(n+1);
for (int i=1;i<=n;i++)
{
cin>>a[i];
e[i].clear();
}
int u,v;
for (int i=1;i<=n-1;i++)
{
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
auto dfs=[&](auto && self,int u,int fa)->void
{
dp[u][0]=0;
dp[u][1]=a[u];
// if (e[u].size()==0)
// return ;
for(auto v:e[u])
{
if(v==fa)continue;
self(self,v,u);
dp[u][0]+=max(dp[v][0],dp[v][1]);
dp[u][1]+=max(dp[v][0],dp[v][1]-2*c);
}
};
dfs(dfs,1,-1);
cout<<max(dp[1][0],dp[1][1])<<"\n";
}
signed main()
{
std::cin.tie(nullptr)->sync_with_stdio(false);
int t;
cin>>t;
while (t--)
{
solve();
}
return 0;
}
H
容易发现 后手 最多是平局。这就需要 这个区间内的 不同的数字 的个数 是偶数。
使用异或哈希来做。(按照我的理解 ,异或哈希 是 随机化算法和 异或和 的组合)
当然可以 直接 将 这个值域内的数 都随机化出来,构建出来hash 表。我下面的代码是 用了map 来记录是否 出现过这个数字。都可以
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve()
{
mt19937 mt(time(0));
int n, q;
cin >> n >> q;
vector<int> a(n + 1);
int t;
map<int, int> mp;
for (int i = 1; i <= n; i++)
{
cin >> t;
if (mp.find(t) == mp.end())
mp[t] = mt();
a[i] = mp[t];
}
for (int i = 1; i <= n; i++)
{
a[i] = a[i] ^ a[i - 1];
}
int l, r;
while (q--)
{
cin >> l >> r;
if ((a[r] ^ a[l - 1]) == 0)
{
cout << "YES\n";
}
else
cout << "NO\n";
}
}
signed main()
{
std::cin.tie(nullptr)->sync_with_stdio(false);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}