2023.02.11 模拟赛小结
2023.02.11 模拟赛小结
更好的阅读体验戳此进入
考的还是比较炸的,前两道已经想的差不多了,不过最后还是挂了。。
赛时思路
T1
#6178. 「美团 CodeM 初赛 Round B」景区路线规划。
签到题,随便设一下状态转移一下即可,然后我大概就是想了很久总觉得这个玩意不符合无后效性,于是写了个十分复杂的分步考虑互相转移两个 DP 分别搞,一个是从该点起始再回到该点恰好时间的概率,然后再转移互相之间的概率,很 nt 的思路,甚至后面的互相转移的过程就是答案的式子,麻了麻了。
最后不知道时正确性的问题还是什么,总之只有 $ 10\texttt{pts} $。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}
using namespace std;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
template < typename T = int >
inline T read(void);
struct Edge{
Edge* nxt;
int to;
int val;
OPNEW;
}ed[26000];
ROPNEW;
Edge* head[110];
int N, M, K;
int c[110], h1[110], h2[110];
int deg[110][500];
ld expA(0.0), expB(0.0);
ld dp[110][500];
ld rdp[110][500];
int main(){
freopen("park.in", "r", stdin);
freopen("park.out", "w", stdout);
N = read(), M = read(), K = read();
for(int i = 1; i <= N; ++i)c[i] = read(), h1[i] = read(), h2[i] = read();
for(int i = 1; i <= M; ++i){
int s = read(), t = read(), v = read();
head[s] = new Edge{head[s], t, v};
head[t] = new Edge{head[t], s, v};
}
for(int p = 1; p <= N; ++p)
for(int j = 0; j <= K; ++j)
for(auto i = head[p]; i; i = i->nxt)
if(j + i->val + c[SON] <= K)++deg[p][j];
for(int p = 1; p <= N; ++p){
dp[p][c[p]] = 1.0 / (ld)N;
for(int cost = 0; cost <= K; ++cost)
for(auto i = head[p]; i; i = i->nxt)
if(deg[p][cost] && deg[SON][cost + i->val + c[SON]] && cost + i->val * 2 + c[SON] + c[p] <= K)
dp[p][cost + i->val * 2 + c[SON] + c[p]] += dp[p][cost] * (1.0 / (ld)deg[p][cost]) * (1.0 / (ld)deg[SON][cost + i->val + c[SON]]);
}
memcpy(rdp, dp, sizeof dp);
for(int p = 1; p <= N; ++p)
for(int j = 0; j <= K; ++j)
for(auto i = head[p]; i; i = i->nxt)
if(deg[p][j] && j + i->val + c[SON] <= K)
rdp[SON][j + i->val + c[SON]] += dp[p][j] * (1.0 / (ld)deg[p][j]);
for(int i = 1; i <= N; ++i)
for(int j = 0; j <= K; ++j)
// printf("dp[%d][%d] = %.5Lf, rdp[%d][%d] = %.5Lf\n", i, j, dp[i][j], i, j, rdp[i][j]),
expA += (ld)h1[i] * rdp[i][j], expB += (ld)h2[i] * rdp[i][j];
printf("%.5Lf %.5Lf\n", expA, expB);
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
/*
5 4 60
25 12 83
30 38 90
16 13 70
22 15 63
50 72 18
2 1 7
3 1 7
4 3 1
5 3 10
*/
T2
想到随机权值,想到哈希,然后考虑将其划分集合之后作个差直接在每个数的随机权值构成的 unordered_set
里查一下,然后想到这东西可能不是差一个数,于是就寄了,也想过直接前缀异或然后二分,但是判不了个数,也想过前缀哈希,但是判不了顺序。
所以最后挂了个暴力上去,$ 30\texttt{pts} $。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}
using namespace std;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
template < typename T = int >
inline T read(void);
int N, Q;
basic_string < int > A;
int main(){
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
int T = read();
while(T--){
A.clear(); A += -1;
N = read(), Q = read();
for(int i = 1; i <= N; ++i)A += read();
while(Q--){
int a = read(), b = read(), c = read(), d = read();
basic_string < int > s1(A.substr(a, b - a + 1)), s2(A.substr(c, d - c + 1));
sort(s1.begin(), s1.end()), sort(s2.begin(), s2.end());
bool diff(false);
for(int i = 0; i < (int)s1.size(); ++i){
if(s1.at(i) != s2.at(i)){
if(!diff)diff = true;
else{printf("NO\n"); break;}
}
if(i == (int)s1.size() - 1)printf("YES\n");
}
}
}
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
/*
1
6 3
1 3 4 2 3 4
1 3 4 6
1 2 5 6
3 5 2 4
*/
T3
这个 OJ 挂了,所以链接也没用了。
给定无向带权完全图,等概率随机生成树,求任意两点间路径长度期望。
部分分很足,详细推导后可以拿到 $ 60\texttt{pts} $,剩下的正解分是一些根 Prufer 序列相关的奇怪东西,不会,当然或者也可以学习 @sssmzy 的高妙做法,发现答案与不连通要求两点的边的边权和一定成线性关系,且仅与 $ n $ 相关,由部分分可知。于是直接通过除法粗略计算斜率,然后实数二分提升精度即可。
正解
T1
如上所说,将多出来的转移删掉然后先枚举时间再枚举点,就可以直接消除后效性了。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}
using namespace std;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
template < typename T = int >
inline T read(void);
struct Edge{
Edge* nxt;
int to;
int val;
OPNEW;
}ed[26000];
ROPNEW;
Edge* head[110];
int N, M, K;
int c[110], h1[110], h2[110];
int deg[110][500];
ld expA(0.0), expB(0.0);
ld dp[110][500];
ld rdp[110][500];
int main(){
// freopen("park.in", "r", stdin);
// freopen("park.out", "w", stdout);
N = read(), M = read(), K = read();
for(int i = 1; i <= N; ++i)c[i] = read(), h1[i] = read(), h2[i] = read();
for(int i = 1; i <= M; ++i){
int s = read(), t = read(), v = read();
head[s] = new Edge{head[s], t, v};
head[t] = new Edge{head[t], s, v};
}
for(int p = 1; p <= N; ++p)
for(int j = 0; j <= K; ++j)
for(auto i = head[p]; i; i = i->nxt)
if(j + i->val + c[SON] <= K)++deg[p][j];
// for(int p = 1; p <= N; ++p){
// dp[p][c[p]] = 1.0 / (ld)N;
// for(int cost = 0; cost <= K; ++cost)
// for(auto i = head[p]; i; i = i->nxt)
// if(deg[p][cost] && deg[SON][cost + i->val + c[SON]] && cost + i->val * 2 + c[SON] + c[p] <= K)
// dp[p][cost + i->val * 2 + c[SON] + c[p]] += dp[p][cost] * (1.0 / (ld)deg[p][cost]) * (1.0 / (ld)deg[SON][cost + i->val + c[SON]]);
// }
// memcpy(rdp, dp, sizeof dp);
for(int i = 1; i <= N; ++i)rdp[i][c[i]] = 1.0 / (ld)N;
for(int j = 0; j <= K; ++j)
for(int p = 1; p <= N; ++p){
// rdp[p][c[p]] = 1.0 / (ld)N;
for(auto i = head[p]; i; i = i->nxt)
if(deg[p][j] && j + i->val + c[SON] <= K)
rdp[SON][j + i->val + c[SON]] += rdp[p][j] * (1.0 / (ld)deg[p][j]);
}
for(int i = 1; i <= N; ++i)
for(int j = 0; j <= K; ++j)
// printf("dp[%d][%d] = %.5Lf, rdp[%d][%d] = %.5Lf\n", i, j, dp[i][j], i, j, rdp[i][j]),
expA += (ld)h1[i] * rdp[i][j], expB += (ld)h2[i] * rdp[i][j];
printf("%.5Lf %.5Lf\n", expA, expB);
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
/*
5 4 60
25 12 83
30 38 90
16 13 70
22 15 63
50 72 18
2 1 7
3 1 7
4 3 1
5 3 10
*/
T2
(权值、动态开点)主席树维护和哈希即可,这样就可以快速处理无序哈希,很朴素的思路,当然我是 sb 所以没想到。
code 懒得补了,主要欠的题太多,以后复习,主席树的时候或许再来写写
T3
代码咕了。
UPD
update-2023_02_11 初稿