【补】2022.7.26———HZOI【暑假多校联训6】 游寄
咱就是说多校的题水
多少分忘了,TLEcoders不想上
T1:Start T2:Dream T3:It T4:Possible
题
大模拟。
很好。很他妈棒。
大模拟都很简单。但是话是这么说。
所以这个题在我重构了遍之后仍然没有掉
这其实是一道构造题。
原串是长的
然后学长给复制了一下变成了长的
所以说,长的串,左边长有个g
、个z
,右边也是
因为他问的是公共子序列,所以不需要连续
很显然地想到左边个g
,右边个z
,这样就凑了个了
最后在末尾加一个g,这样便能普适所有的串
正确性?
ggzz -> ggzzggzz
gzgz -> gzgzgzgz
zgzg -> zgzgzgzg
zzgg -> zzggzzgg
也就是说,不管你怎么卡,你是卡不掉这个构造的
当你限制g和z顺序的时候,右边的复制会把n个z复制出来,这样你又卡不掉了
具体的证明见下发文件的题解,我不会证明,反正确实可以这样构造
代码
#include <iostream>
#include <iomanip>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define _MARK "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ " "
#define Endl cout << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false);cin.tie(NULL), cout.tie(NULL);}
int n;
void work(){
cin >> n;
for (re i = 1 ; i <= n ; ++ i) cout << 'g';
for (re i = 1 ; i <= n ; ++ i) cout << 'z';
cout << 'g' << '\n';
}
// #define IXINGMY
char_phi main(){
#ifdef IXINGMY
FBI_OPENTHEDOOR(a);
#endif
Fastio_setup();
work();
return GMY;
}
考场上以为这是水题,结果爆大零了,伤心
正解有很多,感觉各位都是查分约束或者找规律等等,这里我就说说@的排序
这种做法比较奇特(((
似乎没发现除了之外的人用这个?((
首先缩块,扫一遍把连续着的相同的给变成一个块,然后不相同的自成别的块
然后对小的往大的建有向边,边权为1,因为要平均分最小
嘛
然后就统计所有入度为0(也就是比相邻两边都小的)点,然后扔到队列里,开始跑拓扑
由于拓扑了所以有先后的更新顺序可以保证答案的正确性
实在不懂就造个样例手模
具体实现,代码:
#include <iostream>
#include <iomanip>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define _MARK "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ " "
#define Endl cout << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 1000005
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false);cin.tie(NULL), cout.tie(NULL);}
int n, L, R(-1), star_cnt, kuai_num;
int a[N], head[N], k[N][100], ru[N], ans[N];
struct star{
int v, w, nxt;
}e[N<<1];
struct node{
int id, w;
}q[N];
inline void star_add(int u, int v, int w){e[++ star_cnt].v=v, e[star_cnt].w=w, e[star_cnt].nxt=head[u], head[u]=star_cnt, ++ ru[v];}// w:+1
void work(){
// hxqasd拓扑
// 确实看起来很简单
// 搞!
cin >> n;
for (re i = 1 ; i <= n ; ++ i){
cin >> a[i];
if (a[i] == a[i-1]) k[kuai_num][++ k[kuai_num][0]] = i;
else k[++ kuai_num][++ k[kuai_num][0]] = i;// 开新坑
}
for (re i = 2 ; i <= kuai_num ; ++ i){
if (a[k[i][1]] > a[k[i-1][1]]) star_add(i-1, i, 1);// 一个由小的指向大的
else star_add(i, i-1, 1);// , cout << i << _ << i-1 << '\n'
}
// for (re i = 1 ; i <= kuai_num ; ++ i)
// cout << ru[i] << _;
// Endl;
// for (re i = 1 ; i <= kuai_num ; ++ i){
// MARK, cout << _ << _;
// for (re j = 1 ; j <= k[i][0] ; ++ j){
// cout << k[i][j] << _;
// }
// Endl;
// }
for (re i = 1 ; i <= kuai_num ; ++ i)
if (ru[i] == 0)
q[++ R] = (node){i, 1};
node leader;
while (L <= R){
// MARK;
leader = q[L], ++ L, ans[leader.id] = leader.w;
for (re i = head[leader.id] ; i ; i = e[i].nxt){
ru[e[i].v] --;
if (ru[e[i].v] == 0){
q[++ R] = (node){e[i].v, leader.w+e[i].w};// 精髓在此
}
}
}
for (re i = 1 ; i <= kuai_num ; ++ i){
for (re j = 1 ; j <= k[i][0] ; ++ j){
cout << ans[i] << _;
}
}
Endl;
}
// #define IXINGMY
char_phi main(){
#ifdef IXINGMY
FBI_OPENTHEDOOR(a);
#endif
Fastio_setup();
work();
return GMY;
}
这个题思路巧,+最短路
因为题目中说了,
保证输入的前 n-1 条边是树边,剩下的 m-n+1 条边是返祖边。
所以可以先求出来原树的所有信息来。
首先我们考虑这样一张图,要求的是6~9
的最短路:
的是。
如果完全按照树边走,会得到总和为的路。
设两个点x
,y
,他们的为lca
如果对4的子树(包括4)跑一个,可以获得dis[lca][x]+dis[lca][y]即dis[4][6] + dis[4][9] = 8
总和为的路。
但是显然还有更短的。总和为的路怎么处理?
答案是从lca进一步往上跳。令lca = fa[lca][0]
直到跳到lca = 0
,期间更新的值,也就是
while (lca != 0){
final_ans = MIN(final_ans, dis[lca][x]+dis[lca][y]);
lca = fa[lca][0];
}
至此本题就基本做完了。再汇总一下主要的操作:
- 预处理出倍增数组
fa[][]
(你要是非要用树剖我不拦着你) - 对于每个节点,在把所有边都读入之后跑,注意是对于每个节点的子树(包括节点本身)跑,所以要判断一下下一个要跑的点是否不在当前根节点的子树里,用之前求
fa[][]数组
的dep[]数组
判断一下即可。(跑似乎不行,我也不清楚为什么)
然后代码实现:
#include <iostream>
#include <iomanip>
#include <unordered_map>
#include <queue>
#include <cstring>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define _MARK "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ " "
#define Endl cout << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define ABS(x) (((x) < 0) ? (-(x)) : (x))
#define N 100005
#define M 500005
// #define int long long
#define mod %
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false);cin.tie(NULL), cout.tie(NULL);}
/*
来改T4
wy说的很明白,我尝试代码实现
前n-1条输入的边是树边,所以可以在这里求一个LCA
*/
int n, m, Q, star_cnt;
long long final_ans;
bool vis[N];
int head[N], dep[N], pa[N];
int fa[N][30];
struct star{int v, nxt; long long w;}e[M<<1];
struct node{
int id;
long long w;
friend bool operator < (node A, node B){return A.w > B.w;}// 大于!
};
unordered_map<int, long long> dis[N];// woqu,map能这么用?!O(1)建点?!
priority_queue<node> q;
inline void star_add(int u, int v, long long w){e[++ star_cnt].v=v, e[star_cnt].w=w, e[star_cnt].nxt=head[u], head[u]=star_cnt;}
void get_fa__(int x, int faer){
int k(0);
for (; fa[x][k] != 0 ; ++ k)
fa[x][k+1] = fa[fa[x][k]][k];
pa[x] = k;
for (re i = head[x] ; i ; i = e[i].nxt){
if (e[i].v == faer) continue;
dep[e[i].v] = dep[x]+1, fa[e[i].v][0] = x;
get_fa__(e[i].v, x);
}
}
// 利用递归回溯更新的特性,以及dep数组,求某一个点到他的子树中的点的距离
// 然后
/*random_device seed;
mt19937 myrand(seed());
inline bool comp(node A, node B){return dis[A.x][A.id] < dis[A.x][B.id];}
inline void spfa(int x){
for (re i = 1 ; i <= n ; ++ i) inq[i] = false, dis[x][i] = 999999999999999999;
L = 0, R = -1, dis[x][x] = 0, q[++ R] = (node){x, x}; node now;
while (L <= R){
now = q[L], L ++, inq[now.id] = false;
if ( (myrand() / (((long long)(R-L+1)<<10) + 1) / 100 ) == 0) sort(q+L, q+R+1, comp);
for (re i = head[now.id] ; i ; i = e[i].nxt){
if (dep[e[i].v] < dep[x]) continue;// 下一个点要是x的子树,那下一个点的深度必须要比x大
if (dis[x][e[i].v] > dis[x][now.id]+e[i].w){
dis[x][e[i].v] = dis[x][now.id]+e[i].w;
// cout << "最短路:" << x << _ << e[i].v << '\n';
if (inq[e[i].v] == false)
q[++ R] = (node){x, e[i].v}, inq[e[i].v] = true;
}
}
}
// Endl, Endl;
}*/
inline void dijskra(int x){
// for (re i = 1 ; i <= n ; ++ i) dis[x][i] = 114514191900000, vis[i] = false;
q.push((node){x, 0}), dis[x][x] = 0; node now;
while (q.empty() == false){
now = q.top(), q.pop();
if (vis[now.id] == true) continue;
vis[now.id] = true;
for (re i = head[now.id] ; i ; i = e[i].nxt){
if (dep[e[i].v] < dep[x]) continue;
if(vis[e[i].v]==1)continue;
if (dis[x][e[i].v] > dis[x][now.id]+e[i].w || dis[x][e[i].v] == 0){
dis[x][e[i].v] = dis[x][now.id]+e[i].w;
if (vis[e[i].v] == false){
q.push((node){e[i].v, dis[x][e[i].v]});
}
}
}
}
}
void dfs(int x, int faer){
for (re i = head[x] ; i ; i = e[i].nxt){
if (e[i].v == faer) continue;
if (ABS(dep[x]-dep[e[i].v]) > 1) continue;// 返祖边,不搜过去
// cerr << "dfs: " << x << _ << e[i].v << '\n';
dfs(e[i].v, x);
}
dijskra(x);
memset(vis, false, sizeof(vis));
}
inline int LCA(int x, int y){
// cout << x << _ << y << '\n';
if (dep[x] < dep[y]) swap(x, y);
// cout << x << _ << y << '\n';
for (re i = pa[x] ; i >= 0 ; -- i)
if (dep[fa[x][i]] >= dep[y])
x = fa[x][i];
// cout << x << _ << y << '\n';
if (x == y) return x;
for (re i = pa[x] ; i >= 0 ; -- i)
if (fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
// cout << x << _ << y << '\n';
return fa[x][0];
}
inline void work(){
cin >> n >> m >> Q;
for (re i = 1, uu, vv, ww ; i <= n-1 ; ++ i){cin >> uu >> vv >> ww; star_add(uu, vv, ww), star_add(vv, uu, ww);}
dep[0] = -1;
get_fa__(1, 0);
/* for (re i = 1 ; i <= n ; ++ i){
for (re j = 0 ; fa[i][j] != 0 ; ++ j)
cout << i << _ << j << _ << fa[i][j] << '\n';
Endl;
}
for (re i = 1 ; i <= n ; ++ i)
cout << i << _ << dep[i] << '\n'; */
for (re i = n, uu, vv, ww ; i <= m ; ++ i){cin >> uu >> vv >> ww; star_add(uu, vv, ww), star_add(vv, uu, ww);}// , cout << "u、v:" << uu << _ << vv << '\n'
// NIMA读入竟然错了
dfs(1, 0);
int uer, ver, lca;
while (Q --){
cin >> uer >> ver; lca = LCA(uer, ver), final_ans = 114514191999999999;
final_ans = MIN(final_ans, dis[lca][uer]+dis[lca][ver]);
// cout << "LCA: " << lca << '\n';
// for (; fa[lca][0] != 0 ; lca = fa[lca][0]){
// final_ans = MIN(final_ans, dis[lca][uer]+dis[lca][ver]);
// // cout << "统计长度:" << lca << _ << uer << _ << ver << _ << dis[lca][uer] << _ << dis[lca][ver] << '\n';
// }
while (lca != 0){
final_ans = MIN(final_ans, dis[lca][uer]+dis[lca][ver]);
lca = fa[lca][0];
}
// Endl;
cout << final_ans << '\n';
// Endl, Endl;
}
}
/*WA成0分,造了数据:
7 7 3
1 2 1
2 3 4
2 4 5
4 5 6
4 6 7
1 7 2
6 2 1
1 6
7 6
5 6
*/// 只对了1个 // 额不对是我刚才数据搞错了 这个数据过了
// #define IXINGMY
char_phi main(){
#ifdef IXINGMY
FBI_OPENTHEDOOR(a);
#endif
Fastio_setup();
work();
return GMY;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现