「模拟赛」暑期集训CSP提高模拟11(7.29)
总结写前面:
-
T1 掌握
求次短路长度及个数; -
T2、T3 新思路,都很巧妙;
-
T4 珂朵莉树
A. Fate
评价:数据过水。
赛后听说纯暴力搜索就能过,我交了个暴力代码,
之后丁真造出了 hack 数据,但只卡到了
赛时小丑代码
#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
int n, m, s, t, sr, f[N][2];
int head[N], to[N<<1], nxt[N<<1], tot;
void add(int x, int y){
to[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
int dis[N], vis[N], di[N];
void Dij(int a){
memset(dis, 0x3f3f3f3f, sizeof dis);
memset(vis, false, sizeof vis);
priority_queue<pair<int, int> >q;
dis[a] = 0;
q.push(mp(-dis[a], a));
while(q.size())
{
int x = q.top().second;
q.pop(), vis[x] = 1;
for(int i=head[x]; i; i=nxt[i]){
int y = to[i];
if(dis[y] > dis[x] + 1){
dis[y] = dis[x] + 1;
if(!vis[y]) q.push(mp(-dis[y], y));
}
}
}
}
bool fl[N];
int dfs(int now, int p, int len){
if(now == t and len == dis[t] + 1) return 1;
if(now == t) return 0;
if(len == dis[t] + 1) return 0;
ll ans = 0;
for(int i=head[now]; i; i=nxt[i]){
int y = to[i];
if(fl[y]) continue;
fl[y] = true;
ans = (ans + dfs(y, now, len+1)) % mod;
fl[y] = false;
}
return ans;
}
void Dij_Aqr(int a){
memset(di, 0x3f3f3f3f, sizeof di);
memset(vis, false, sizeof vis);
priority_queue<pair<int, int> >q;
di[a] = 0;
q.push(mp(-di[a], a));
while(q.size())
{
int x = q.top().second;
q.pop(), vis[x] = 1;
for(int i=head[x]; i; i=nxt[i]){
int y = to[i];
if(di[x] + 1 == dis[y] + 1) f[y][1]++;
if(di[y] > di[x] + 1){
di[y] = di[x] + 1;
}
if(!vis[y]) q.push(mp(-di[y], y));
}
}
}
int main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>m>>s>>t;
for(int i=1; i<=m; i++){
int x, y; cin>>x>>y;
add(x, y), add(y, x);
}
Dij(s); sr = dis[t];
if(n <= 100){
fl[s] = true;
cout<<dfs(s, 0, 0);
return 0;
}
Dij_Aqr(s);
cout<<f[t][1];
return 0;
}
题意:
正解:
显然我们需要跑一边 Dij 求最短路径长度,我们可以在求最短路的同时维护出次短路的长度及个数,那么我们最后特判一下次短路长度是否为最短路长度
code:
#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
using namespace std;
const int N = 4e5 + 10;
const int mod = 1e9 + 7;
int n, m, s, t, sr, dis[N][2], cnt[N][2], vis[N][2];
int head[N], to[N<<1], nxt[N<<1], tot;
void add(int x, int y){
to[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
void Dij_Aqr(int a){
memset(dis, 0x3f, sizeof dis);
memset(vis, false, sizeof vis);
priority_queue<pair<int, pair<int, int>> >q;
dis[a][0] = 0, cnt[a][0] = 1;
q.push(mp(0, mp(a, 0)));
while(q.size())
{
int x = q.top().second.first, tag = q.top().second.second;
q.pop();
if(vis[x][tag]) continue;
vis[x][tag] = 1;
for(int i=head[x]; i; i=nxt[i]){
int y = to[i];
if(dis[y][0] > dis[x][tag] + 1){
dis[y][1] = dis[y][0];
dis[y][0] = dis[x][tag] + 1;
cnt[y][1] = cnt[y][0];
cnt[y][0] = cnt[x][tag];
q.push(mp(-dis[y][1], mp(y, 1)));
q.push(mp(-dis[y][0], mp(y, 0)));
}
else if(dis[y][0] == dis[x][tag] + 1) cnt[y][0] = (cnt[y][0] + cnt[x][tag]) % mod;
else if(dis[y][1] > dis[x][tag] + 1){
dis[y][1] = dis[x][tag] + 1;
cnt[y][1] = cnt[x][tag];
q.push(mp(-dis[y][1], mp(y, 1)));
}
else if(dis[y][1] == dis[x][tag] + 1) cnt[y][1] = (cnt[y][1] + cnt[x][tag]) % mod;
}
}
}
int main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>m>>s>>t;
for(int i=1; i<=m; i++){
int x, y; cin>>x>>y;
add(x, y), add(y, x);
}
Dij_Aqr(s);
if(dis[t][1] == dis[t][0] + 1)
cout<<cnt[t][1];
else cout<<0;
return 0;
}
B.EVA 45 pts
赛时打的
题意:
总共有
可以选择一个时刻
正解:
枚举每一条鱼为网的左端点(网的左端点在一条鱼上一定最不劣),计算你其他所有鱼进入和出去这个网的时间,按升序排列,然后按时间顺序遍历每一个事件,如果是有鱼进入网内,则加上这条鱼的贡献,有鱼离开网的范围则减去该鱼的贡献。(计算鱼进出的时间可以用相对运动,将作网左端点的鱼视为静止,我是根据式子直接计算)。
code:
#include<bits/stdc++.h>
#define x(i) a[i].x
#define w(i) a[i].w
#define v(i) a[i].v
#define mp make_pair
using namespace std;
const int N = 4e3 + 10;
const double MAX = 2e9;
int n, len;
struct fish{
double x; int v, w;
}a[N];
struct area{
double shu; int wa, f;
bool operator < (const area &A) const{
if(shu == A.shu){
return f > A.f;
}
return shu < A.shu;
}
}t[N];
pair<double, double> solve(int i, int j){
if(x(j) < x(i) and v(j) <= v(i)) return mp(1.0, 0.0);
if(x(i) < x(j) - len and v(i) <= v(j)) return mp(1.0, 0.0);
double l = 0, r = 0;
if(x(i) < x(j)){
if(v(i) > v(j)){
r = (x(j) - x(i)) / (v(i) - v(j));
l = max(0.0, ( (len + x(i) - x(j)) / (v(j) - v(i)) ) );
return mp(l, r);
}
if(v(i) == v(j)) return mp(0, MAX);
if(v(i) < v(j)){
r = (len + x(i) - x(j)) / (v(j) - v(i));
return mp(0, r);
}
}
else{
l = (x(i) - x(j)) / (v(j) - v(i));
r = (len + x(i) - x(j)) / (v(j) - v(i));
return mp(l, r);
}
return mp(0.0, 0.0);
}
int main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>len; int ans = 0;
for(int i=1; i<=n; i++){
cin>>w(i)>>x(i)>>v(i);
ans = max(ans, w(i));
}
for(int i=1; i<=n; i++){ // left_fish
memset(t, 0, sizeof t); int cnt = 0;
for(int j=1; j<=n; j++){
if(i == j) continue;
double l = 0, r = 0;
if(x(i) == x(j) and v(i) == v(j)) l = 0, r = MAX;
else{
pair<double, double> now = solve(i, j);
if(now.first > now.second) continue;
l = now.first, r = now.second;
}
t[++cnt] = {l, w(j), 1};
t[++cnt] = {r, w(j), -1};
}
sort(t+1, t+1+cnt); int hnt = w(i);
for(int j=1; j<=cnt; j++){
hnt += t[j].wa * t[j].f;
ans = max(ans, hnt);
}
}
cout<<ans;
return 0;
}
C.嘉然登场
思路很巧妙的题!值得以后复习。
赛时大致思路很接近,我已经可以往正解的方向去想了,但是具体如何死活想不出来,还是见得少,没有解决这种题型的经验。
数据中有
正解:
根据 0、1 数据的引导,我们容易想到将序列分为
然后处理每一个小于
接着从大到小遍历 T++;
,并处理所有与这个数挂钩的数,挂钩的数肯定是 T--;
。
最后因为可能会有相同的数,于是需要去重:答案除去每个相同的数的个数的阶乘。
code:
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define mp make_pair
using namespace std;
const int N = 2e5 + 10;
const int mod = 998244353;
int n, k, K, a[N], jie[N];
map<int, int>kind;
map<int, int>to;
int qpow(ll A){
int b = mod - 2; ll res = 1;
while(b)
{
if(b & 1) res = res * A % mod;
A = A * A % mod;
b >>= 1;
}
return res;
}
signed main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>K; k = (K + 1) / 2;
for(int i=1; i<=n; i++) cin>>a[i], kind[a[i]]++;
sort(a+1, a+1+n);
for(int i=1; i<=n; i++){
if(a[i] >= k) break;
int y = lower_bound(a+1, a+1+n, K-a[i]) - a;
to[y]++;
}
int T = 1; ll ans = 1;
for(int i=n; i>=1; i--){
if(a[i] >= k){
// cout<<a[i]<<" "<<to[a[i]]<<"\n";
ans = ans * T % mod, T++;
for(int j=1; j<=to[i]; j++){
ans = ans * T % mod, T--;
}
}
else break;
}
ll jie = 1;
for(int i=1; i<=n; i++){
for(int j=1; j<=kind[a[i]]; j++){
jie = jie * j % mod;
}
kind[a[i]] = 0;
}
cout<<ans*qpow(jie)%mod;
return 0;
}
D.Clannad
题意:
略...
正解:
两种做法,不细讲了(因为贺的题解,没那么懂)
-
珂朵莉树+树状数组+lca
-
回滚莫队
放一下珂朵莉树代码。
code:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m, Q;
int a[N], st[N][20], ans[N];
pair<int, int>q[N];
vector<int>vec[N];
struct BIT{
int v[N];
void add(int x, int y){
assert(x);
for(; x<=m; x+=x&-x) v[x] += y;
}
int ask(int x){
int res = 0;
for(; x; x-=x&-x) res += v[x];
return res;
}
}T;
int head[N], to[N<<1], nxt[N<<1], tot;
void add(int x, int y){
to[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
int dep[N], size[N], heavy[N], fa[N];
void dfs(int x, int p){
size[x] = 1, dep[x] = dep[p] + 1, fa[x] = p;
for(int i=head[x]; i; i=nxt[i]){
int y = to[i];
if(y == p) continue;
dfs(y, x);
size[x] += size[y];
if(size[y] > size[heavy[x]]) heavy[x] = y;
}
}
int dfn[N], top[N], num, edfn[N];
void dfsn(int x, int tp){
dfn[x] = ++num, top[x] = tp, edfn[num] = x;
if(!heavy[x]) return;
dfsn(heavy[x], tp);
for(int i=head[x]; i; i=nxt[i]){
int y = to[i];
if(y == fa[x] or y == heavy[x]) continue;
dfsn(y, y);
}
}
int glca(int x, int y){
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
if(dep[x] < dep[y]) swap(x, y);
return y;
}
int qlca(int l, int r){
int k = log2(r - l + 1);
return glca(st[l][k], st[r-(1<<k)+1][k]);
}
struct ODT{
int l, r;
mutable int v;
ODT(const int &tl, const int &tr, const int &tv){l = tl, r = tr, v = tv;}
bool operator < (const ODT &A)const{return l < A.l;}
};
set<ODT>odt;
typedef set<ODT>::iterator iter;
iter spilt(int x){
iter it = --odt.upper_bound((ODT){x, 0, 0});
if(it -> l == x) return it;
assert(it != odt.end()); // if need go on,or will return
int l = it -> l, r = it -> r, v = it -> v;
odt.erase(it); odt.insert((ODT){l, x-1, v});
return odt.insert((ODT){x, r, v}).first;
}
void assign(int l, int r, int v){
iter itr = spilt(r + 1), itl = spilt(l);
for(auto i=itl; i!=itr; i++) T.add(m-i->v+1, -(i->r-i->l+1));
odt.erase(itl, itr); // erase the all area in [itl, itr).
odt.insert((ODT){l, r, v});
T.add(m-v+1, r-l+1);
}
void update(int x, int tim){
while(x)
{
assign(dfn[top[x]], dfn[x], tim);
x = fa[top[x]];
}
}
int main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>m>>Q;
odt.insert((ODT){1, n, 0});
for(int i=1; i<=n-1; i++){
int x, y; cin>>x>>y;
add(x, y), add(y, x);
}
for(int i=1; i<=m; i++){
cin>>a[i]; st[i][0] = a[i];
}
dfs(1, 0), dfsn(1, 1);
for(int i=1; i<=17; i++){
for(int j=1; j+(1<<i)-1<=m; j++){
st[j][i] = glca(st[j][i-1], st[j+(1<<i-1)][i-1]);
}
}
for(int i=1; i<=Q; i++){
cin>>q[i].first>>q[i].second;
ans[i] = dep[1] - dep[qlca(q[i].first, q[i].second)];
vec[q[i].second].emplace_back(i);
// cout<<dep[1]<<"\n";
}
for(int i=1; i<=m; i++){
update(a[i], i);
for(auto j:vec[i]) ans[j] += T.ask(m - q[j].first + 1);
}
for(int i=1; i<=Q; i++) cout<<ans[i]<<"\n";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】