清华集训2017 Day 2简要题解
*注意:这套题目题面请在loj / uoj查看
Problem A 小 Y 和地铁
题目传送门
题目大意
小Y乘坐0号地铁线路。城市中有不超过$n + 1$条地铁线路。地铁线路不会自交,两条不同的地铁线路最多有两个交点,交点处一定是换乘站,同时也没有三条地铁线路交于一点。小Y经过了$n$个换乘站,依次给通过它们能过换到的另一条地铁线路的编号,问城市中之多还有多少个换乘站。
显然只与0号地铁相交1次的地铁线路没有用。
下面的点指的是一个换乘站,它的颜色编号指的是它能够换乘的另一条地铁的编号。
然后认真画图可以发现,连接两个颜色相同的点,实际上会将整个图划分成两个区域,所以总共有四种不同的连线方法:

于是有了$2^{45}\times 45$的优秀做法。
我们考虑从小到达考虑每种颜色的第一个点,我们发现按照对后面的贡献可以将决策分为两种:

每一行的两种状态对后面的影响是一样的,所以我们可以对每一行的两种决策对当前的影响取最小值继续进行搜索。
这样时间复杂度降为$O(Tn2^{n/2})$。
加一个最优性剪枝即可通过。
Code
/**
* loj
* Problem#2323
* Accepted
* Time: 3349ms
* Memory: 252k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; const int N = ; int T;
int n;
int ar[N], br[N], hs[N];
int cup, cdn;
int ups[N], dns[N]; inline void init() {
scanf("%d", &n);
memset(hs, , sizeof(hs));
memset(ar, , sizeof(ar));
for (int i = ; i <= n; i++) {
scanf("%d", br + i);
ar[hs[br[i]]] = i, hs[br[i]] = i;
}
} int res;
void dfs(int p, int cur) {
if (cur >= res)
return ;
if (p > n) {
res = cur;
return ;
}
if (!ar[p])
dfs(p + , cur);
else {
int cnt1 = , cnt2 = ;
for (int i = ; i < cup; i++) {
cnt1 += (ups[i] > p && ups[i] < ar[p]);
cnt2 += (ups[i] > ar[p]);
}
for (int i = ; i < cdn; i++)
cnt2 += (dns[i] > p);
ups[cup++] = ar[p];
dfs(p + , cur + min(cnt1, cnt2)); cup--, cnt1 = , cnt2 = ;
for (int i = ; i < cdn; i++) {
cnt1 += (dns[i] > p && dns[i] < ar[p]);
cnt2 += (dns[i] > ar[p]);
}
for (int i = ; i < cup; i++)
cnt2 += (ups[i] > p);
dns[cdn++] = ar[p];
dfs(p + , cur + min(cnt1, cnt2));
cdn--;
}
} inline void solve() {
res = ;
dfs(, );
printf("%d\n", res);
} int main() {
scanf("%d", &T);
while (T--) {
init();
solve();
}
return ;
}
Problem A
Problem B 小 Y 和二叉树
显然度数小于3的最小的点是中序遍历的第一个点,记它为$R$。(因为可以构造方案)
当主动进入一个以点$s$为根子树中时,那么第一个点一定是其中标号最小的度数小于3的点,设它的标号为$f_{s}$。
剩下的就是贪心。首先从$R$开始贪,如果它的度数为2,那么从它的左右子树中选取$f$值较小的作为右子树。
然后大概就是这样大力分类讨论。
主动进入子树和跳父亲的讨论是不一样的,所以要写两个dfs。
Code
/**
* loj
* Problem#2324
* Accepted
* Time: 2486ms
* Memory: 86200k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; typedef class IO {
protected:
const static int Limit = ;
FILE* file; int ss, st;
char buf[Limit];
public:
IO():file(NULL) { };
IO(FILE* file):file(file) { } void open(FILE *file) {
this->file = file;
} char pick() {
if (ss == st)
st = fread(buf, , Limit, file), ss = ;//, cerr << "Str: " << buf << "ED " << st << endl;
return buf[ss++];
}
}IO; #define digit(_x) ((_x) >= '0' && (_x) <= '9') IO& operator >> (IO& in, int& u) {
char x;
while (~(x = in.pick()) && !digit(x));
for (u = x - ''; ~(x = in.pick()) && digit(x); u = u * + x - '');
return in;
} IO in(stdin); const int N = 1e6 + ;
const signed inf = (signed) (~0u >> ); int n;
int rt, tp = ;
int f[N], deg[N];
vector<int> g[N];
int ans[N]; inline void init() {
in >> n;
for (int i = ; i <= n; i++) {
in >> deg[i];
if (deg[i] < && !rt)
rt = i;
for (int j = , x; j < deg[i]; j++) {
in >> x;
g[i].push_back(x);
}
}
} int cnt = ;
void dp(int p, int fa) {
cnt++;
f[p] = ((deg[p] < ) ? (p) : (inf));
for (int i = , e; i < deg[p]; i++)
if ((e = g[p][i]) != fa) {
dp(e, p);
f[p] = min(f[p], f[e]);
}
} #define s1 son[0]
#define s2 son[1] void dfs1(int p, int fa) { // a certain subtree
int son[], top = ;
for (int i = , e; i < deg[p]; i++)
if ((e = g[p][i]) != fa)
son[top++] = e;
if (!top) {
ans[tp++] = p;
} else if (top == ) {
if (p < f[s1])
ans[tp++] = p, dfs1(s1, p);
else
dfs1(s1, p), ans[tp++] = p;
} else {
if (f[s1] < f[s2])
dfs1(s1, p), ans[tp++] = p, dfs1(s2, p);
else
dfs1(s2, p), ans[tp++] = p, dfs1(s1, p);
}
} void dfs2(int p, int fa) { // haven't known which node is root yet
int son[], top = ;
for (int i = , e; i < deg[p]; i++)
if ((e = g[p][i]) != fa)
son[top++] = e;
ans[tp++] = p;
if (!top)
return;
if (top == ) {
if (s1 <= f[s1])
dfs2(s1, p);
else
dfs1(s1, p);
} else {
if (f[s1] < f[s2])
dfs1(s1, p), dfs2(s2, p);
else
dfs1(s2, p), dfs2(s1, p);
}
} inline void solve() {
dp(rt, );
dfs2(rt, );
for (int i = ; i < n; i++)
printf("%d ", ans[i]);
} int main() {
init();
solve();
return ;
}
Problem B
Problem C 小 Y 和恐怖的奴隶主
直接做期望不好做(就是傻逼想直接算期望,然后发现非常难去掉非法转移),我们考虑计算概率,每次转移的时候更新期望。这样就能把转移化成乘上某个矩阵。
然后发现多组询问,很GG。我们倍增一下,向量乘矩阵就行了。
这道题交uoj可能需要优化一下,某larryzhong出极限数据卡人。
我们来讲讲这道题怎么卡常:
- 矩阵不要开结构体。
- 矩阵乘法直接写代码乘,不写函数或重载运算符
- 矩阵乘法时取大小模数,小模数是题目模数,大模数是在1次乘法中不会爆掉的模数,并且必须是小模数的倍数,然后每次矩阵乘法中的乘直接乘,加变成在大模数意义下加,运算完后再将所有数对小模数取模。
- 加的时候加取模优化。
- 矩阵乘法时使用恰当的循环顺序改善内存访问。
然后就可以跑在std前面了。
Code
/**
* uoj
* Problem#340
* Accepted
* Time: 2267ms
* Memory: 14264k
*/
#include <bits/stdc++.h>
#ifndef WIN32
#define Auto "%lld"
#else
#define Auto "%I64d"
#endif
using namespace std;
typedef bool boolean;
#define ll long long const int M = ;
const ll Mod = 3ll * M * M;
const int bzmax = ; ll Add(ll a, ll b) {
return ((a += b) >= Mod) ? (a - Mod) : (a);
} const int N = ; int qpow(int a, int p) {
if (p < )
p += M - ;
ll rt = , pa = a;
for ( ; p; p >>= , pa = pa * pa % M)
if (p & )
rt = rt * pa % M;
return rt;
} int T;
ll n;
int m, K;
int cS = ;
int id[][][];
ll v1[N], v2[N];
ll *f, *bf;
ll powg[bzmax][N][N]; int addServlant(int x, int y, int z) {
if (x + y + z == K)
return id[x][y][z];
return id[x + (m == )][y + (m == )][z + (m == )];
} inline void init() {
scanf("%d%d%d", &T, &m, &K); for (int i = ; i <= K; i++)
for (int j = ; i + j <= K && (m >= || !j); j++)
for (int k = ; i + j + k <= K && (m == || !k); k++)
id[i][j][k] = cS++;
cS++; ll (*g)[N] = powg[];
for (int i = ; i <= K; i++)
for (int j = ; i + j <= K && (m >= || !j); j++)
for (int k = ; i + j + k <= K && (m == || !k); k++) {
int l = i + j + k + , s = id[i][j][k];
ll invl = qpow(l, -);
if (i)
g[s][id[i - ][j][k]] = invl * i % M;
if (j)
g[s][addServlant(i + , j - , k)] = invl * j % M;
if (k)
g[s][addServlant(i, j + , k - )] = invl * k % M;
g[s][s] = invl, g[s][cS - ] = invl;
}
g[cS - ][cS - ] = ; for (int i = ; i < bzmax; i++) {
ll (*A)[N] = powg[i - ], (*B)[N] = powg[i];
for (int x = ; x < cS; x++)
for (int k = ; k < cS; k++)
for (int y = ; y < cS; y++)
B[x][y] = Add(B[x][y], A[x][k] * A[k][y]);
for (int x = ; x < cS; x++)
for (int y = ; y < cS; y++)
B[x][y] %= M;
}
// powg[i] = powg[i - 1] * powg[i - 1];
} inline void solve() {
f = v1, bf = v2;
while (T--) {
scanf(Auto, &n);
memset(f, , sizeof(ll) * N);
f[id[(m == )][(m == )][(m == )]] = ;
for (int i = ; i < bzmax; i++)
if ((n >> i) & ) {
memset(bf, , sizeof(ll) * N);
for (int x = ; x < cS; x++)
for (int k = ; k < cS; k++)
bf[k] = Add(bf[k], f[x] * powg[i][x][k]);
for (int x = ; x < cS; x++)
bf[x] %= M;
swap(f, bf);
}
printf("%d\n", f[cS - ]);
}
} int main() {
init();
solve();
return ;
}
Problem C
清华集训2017 Day 2简要题解的更多相关文章
- 【UOJ#340】【清华集训2017】小 Y 和恐怖的奴隶主(矩阵快速幂,动态规划)
[UOJ#340][清华集训2017]小 Y 和恐怖的奴隶主(矩阵快速幂,动态规划) 题面 UOJ 洛谷 题解 考虑如何暴力\(dp\). 设\(f[i][a][b][c]\)表示当前到了第\(i\) ...
- [LOJ#2330]「清华集训 2017」榕树之心
[LOJ#2330]「清华集训 2017」榕树之心 试题描述 深秋.冷风吹散了最后一丝夏日的暑气,也吹落了榕树脚下灌木丛的叶子.相识数年的Evan和Lyra再次回到了小时候见面的茂盛榕树之下.小溪依旧 ...
- [LOJ#2329]「清华集训 2017」我的生命已如风中残烛
[LOJ#2329]「清华集训 2017」我的生命已如风中残烛 试题描述 九条可怜是一个贪玩的女孩子. 这天她在一堵墙钉了 \(n\) 个钉子,第 \(i\) 个钉子的坐标是 \((x_i,y_i)\ ...
- [LOJ#2328]「清华集训 2017」避难所
[LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所 ...
- [LOJ#2327]「清华集训 2017」福若格斯
[LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最 ...
- [LOJ#2326]「清华集训 2017」简单数据结构
[LOJ#2326]「清华集训 2017」简单数据结构 试题描述 参加完IOI2018之后就是姚班面试.而你,由于讨厌物理.并且想成为乔布斯一样的创业家,被成功踢回贵系. 转眼,时间的指针被指向201 ...
- [LOJ#2324]「清华集训 2017」小Y和二叉树
[LOJ#2324]「清华集训 2017」小Y和二叉树 试题描述 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙 ...
- [LOJ#2323]「清华集训 2017」小Y和地铁
[LOJ#2323]「清华集训 2017」小Y和地铁 试题描述 小Y是一个爱好旅行的OIer.一天,她来到了一个新的城市.由于不熟悉那里的交通系统,她选择了坐地铁. 她发现每条地铁线路可以看成平面上的 ...
- Loj #2331. 「清华集训 2017」某位歌姬的故事
Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...
随机推荐
- GBDT 详解分析 转+整理
GBDT DT 回归树 Regression Decision Tree 梯度迭代 GBDT工作过程实例 需要解释的三个问题 - 既然图1和图2 最终效果相同,为何还需要GBDT呢? - Gradie ...
- <?php if($value['udertype'] == 0) {?> <td>超级管理员</td> <?php } else if ($value['udertype'] == 1)
<?php if($value['udertype'] == 0) {?> <td>超级管理员</td> <?php } else if ($value['u ...
- 网站favicon图标的显示问题
今天在微信开发者工具发现一个错误,说是找不到favicon.ico这个文件. 这个就是标签式浏览器显示在页面title前面的小图标,移动端也没什么用,所以一直没在意,今天有空就研究了一下,发现还是有点 ...
- myBatis框架_关于怎么获得多表查询的总记录数
<!-- 查找总记录数 --> <select id="billCount" resultType="int"> select coun ...
- python全栈开发 * 08知识点汇总 * 180608
08知识点梳理 文件操作一 .文件操作 r (只读)1.r (读) rb(字节)f=open("果蔬大杂烩",mode="r",encoding="U ...
- Nginx之编译安装的nginx加入systemctl
编译安装的nginx需要添加rc.local 编译安装后设置 /usr/lib/systemd/system/nginx.service [Unit] Description=nginx After= ...
- F#周报2019年第3期
新闻 SAFE最近的活动 什么开源项目适合我们的奖学金受益者上手工作 布署SAFE应用至Google Cloud AppEngine Alea GPU:使用F#进行GPU编程 Rider 2018.3 ...
- webservice接口测试wsdl
http和webservice接口测试有什么区别? webservice的基础组成是http+xml 三要素:soap传输协议,uddi,wsdl(webservice描述语言xml格式) 优点:跨平 ...
- 17.4-uC/OS-III消息管理(任务消息队列使用)
任务消息队列跟任务信号量一样,均隶属于某一个特定任务, 不需单独创建,任务在则在, 只有该任务才可以接收这个任务消息队列的消息,其他任务只能给这个任务消息队列发送消息, 却不能接收.任务消息队列与(普 ...
- SqlServer 行转列,列转行 以及PIVOT函数快速实现行转列,UNPIVOT实现列转行
一 .列转行 创建所需的数据 CREATE TABLE [StudentScores]( [UserName] NVARCHAR(20), --学生姓名 [Subject] NVARCHAR(3 ...