就当我没做过这套题

而且 T3 也不会


Day2 A. 游戏

> Link 游戏 - LOJ

做过 2-sat 的读者应该能够一眼秒出这道题的正解 —— \(\mathcal O(2^d)\) 枚举 x 的取值,其余还没有唯一确定方案的点是一个 2-sat 问题,总复杂度 \(\mathcal O(2^dm)\),边搜索边剪枝,常数很小。

但是为什么这道题可以出在 NOI 里呢?因为它的细节简直恶心。

大概说一下我对几个细节的处理方法:

  • 若 \((i,h_i,j,h_j)\) 中 \(S_i=h_i\),则该条件无效,在之后的搜索中保证不会访问 \(i=S_i\) 这种不合法的点。

  • 若条件 \((i,h_i,j,h_j)\) 中 \(h_j=S_j\),则说明 \(i\) 不能取 \(h_i\);处理方法是建出限制关系的反图,从每个 \(ans_i=S_i\) 的状态往回找前驱状态,把这些状态都 ban 掉。

  • x 的点本身只有两种取值,因此对于条件 \((i,h_i,j,h_j)\),若 \(S_i\neq \text x\),则可以判断「当 \(j\) 不取 \(h_j\) 时,\(i\) 取除了 \(h_i,S_i\) 之外的值」。

  • 在 tarjan 缩点的过程中在搜到一个已确定方案的点 \(v\) 时,经过上述处理,此时一定合法,可以跳过对 \(v\) 的搜索;因为当前状态一定是一个只有两种取值(非真即假)的状态,而在上述第三条中,若 \(v\) 的取值限制了当前点的值,则当前点必然已经固定取值。


Day2 B. 蔬菜

> Link 蔬菜 - LOJ

第一步非常关键的转化(也是我没想出正解的原因):把一种蔬菜再拆成两种 —— 最后一个和其他。为什么是最后一个而不是第一个?因为最后一个的保质期最长,最容易放下来。

为了方便理解可以把「其他」再按保质期不同拆成若干种,但是实现代码时显然不会这么实现,存不下。

于是现在的问题就是「有若干类物品,物品 \(i\) 有 \(C_i\) 个,单位价值为 \(V_i\),只能放在前 \(T_i\) 个篮子里;每个篮子只能装 \(m\) 个物品,最大化放入篮子的物品总价值」。这个贪心比较简单,先放单位价值大的物品,能放则放,不能放则跳过。反证即可说明。

判断能不能放,可以用并查集维护在某个篮子之前最靠后的未满篮子。

每次做一遍贪心复杂度 \(\mathcal O(kpm)\) 或者 \(\mathcal O(kpm\log p)\)。

考虑增加一个篮子,放入的物品会如何变化。结论是在原来已放入物品的基础上再添加。本来可以直接拿拟阵证明,但是我不会,还是用常规的反证方法:假设原本最优解放入了物品 \(x\),但在新最优解中不可能有物品 \(x\),说明篮子 \(1\sim T_x\) 中 的所有物品的单位价值都大于 \(x\),与 \(x\) 在原本最优解中矛盾。

所以说,每次询问 \(p_i\) 其实只是限制了最优解的物品总数不超过 \(mp_i\)。可以贪心处理出最优解中有 \(i\) 个(\(1\le i\le m\cdot\max\{p\}\))物品的最优解,直接输出即可。


源代码

点击展开/折叠 game.cpp
/*Lucky_Glass*/
#include <cstdio>
#include <cstring>
#include <algorithm> #define con(typ) const typ &
const int N = 5e4 + 10, M = 1e5 + 10; inline int rin(int &r) {
int b = 1, c = getchar(); r = 0;
while ( c < '0' || '9' < c ) b = c == '-' ? -1 : b, c = getchar();
while ( '0' <= c && c <= '9' ) r = r * 10 + (c ^ '0'), c = getchar();
return r *= b;
}
inline char rch(char &r) {
char str[20] = {};
scanf("%s", str);
return r = str[0];
} int n, m, nd, nrol;
char str[N];
int xpos[10], typ[N], mex[5][5], col[N], rol[N]; template<const int P, const int E> struct Graph {
int head[P], to[E], nxt[E], ncnt;
void addEdge(con(int) u, con(int) v) {
int p = ++ncnt;
to[p] = v, nxt[p] = head[u];
head[u] = p;
}
inline int operator [] (con(int) u) {return head[u];}
}; Graph<N * 3, M * 3> gr, rg;
Graph<N * 3, N * 3> bel; int nscc, ndfn, nstk;
int dfn[N * 3], low[N * 3], scc[N * 3], stk[N * 3];
bool bok[N * 3], ban[N * 3]; void init() {
mex[0][0] = 1, mex[0][1] = 2, mex[0][2] = 1;
mex[1][0] = 2, mex[1][1] = 0, mex[1][2] = 0;
mex[2][0] = 1, mex[2][1] = 0, mex[2][2] = 0;
}
bool dfs(con(int) u) {
int iu = u / 3, cu = u % 3;
if ( cu == typ[iu] ) return false;
if ( ~col[iu] ) return cu == col[iu];
col[iu] = cu, rol[++nrol] = iu;
for (int it = gr[u]; it; it = gr.nxt[it]) {
int v = gr.to[it];
if ( !dfs(v) )
return false;
}
return true;
}
void tarjan(con(int) u) {
if ( ~col[u / 3] ) return;
dfn[u] = low[u] = ++ndfn;
stk[++nstk] = u, bok[u] = true;
for (int it = gr[u]; it; it = gr.nxt[it]) {
int v = gr.to[it];
if ( ~col[v / 3] ) continue;
if ( !dfn[v] ) tarjan(v), low[u] = std::min(low[u], low[v]);
else if ( bok[v] ) low[u] = std::min(low[u], dfn[v]);
}
if ( low[u] == dfn[u] ) {
int tmp = -1; bel.head[++nscc] = 0;
do {
tmp = stk[nstk--];
bok[tmp] = false;
scc[tmp] = nscc;
bel.addEdge(nscc, tmp);
} while ( tmp != u );
}
}
void fixXPos(con(int) d) {
if ( d > nd ) {
for (int i = 1; i <= n; ++i) if ( col[i] == -1 )
for (int c = 0; c < 3; ++c)
dfn[i * 3 + c] = 0;
ndfn = nscc = bel.ncnt = 0;
for (int i = 1; i <= n; ++i) if ( col[i] == -1 ) {
int p = -1, q = -1;
for (int c = 0; c < 3; ++c) if ( !ban[i * 3 + c] ) {
if ( !dfn[i * 3 + c] ) tarjan(i * 3 + c);
if ( ~p ) q = c;
else p = c;
}
if ( scc[i * 3 + p] == scc[i * 3 + q] )
return;
}
for (int i = 1; i <= nscc; ++i) {
bool tag = true;
for (int it = bel[i]; it; it = bel.nxt[it])
if ( ~col[bel.to[it] / 3] ) {
tag = false;
break;
}
if ( !tag ) continue;
for (int it = bel[i]; it; it = bel.nxt[it])
col[bel.to[it] / 3] = bel.to[it] % 3;
}
for (int i = 1; i <= n; ++i)
putchar('A' + col[i]);
putchar('\n');
exit(0);
}
int u = xpos[d];
if ( ~col[u] ) {
fixXPos(d + 1);
return;
}
for (int c = 0; c < 3; ++c) {
int mrol = nrol;
if ( dfs(u * 3 + c) ) fixXPos(d + 1);
while ( nrol > mrol ) col[rol[nrol--]] = -1;
}
}
void doBan(con(int) u) {
ban[u] = true;
for (int it = rg[u]; it; it = rg.nxt[it] )
if ( !ban[rg.to[it]] )
doBan(rg.to[it]);
}
int main() {
// freopen("input.in", "r", stdin);
init();
rin(n), rin(nd);
scanf("%s", str + 1);
for (int i = 1, tmp = 0; i <= n; ++i)
if ( str[i] == 'x' )
xpos[++tmp] = i, typ[i] = -1;
else
typ[i] = str[i] - 'a';
rin(m);
for (int i = 1; i <= m; ++i) {
char tmp;
int u = rin(u), ut = rch(tmp) - 'A', v = rin(v), vt = rch(tmp) - 'A';
if ( ut == typ[u] ) continue;
gr.addEdge(u * 3 + ut, v * 3 + vt);
rg.addEdge(v * 3 + vt, u * 3 + ut);
if ( ~typ[u] )
for (int j = 0; j < 3; ++j)
if ( j != vt ) {
gr.addEdge(v * 3 + j, u * 3 + mex[typ[u]][ut]);
rg.addEdge(u * 3 + mex[typ[u]][ut], v * 3 + j);
}
}
for (int i = 1; i <= n; ++i)
if ( ~typ[i] )
doBan(i * 3 + typ[i]);
for (int i = 1; i <= n; ++i)
if ( ban[i * 3] && ban[i * 3 + 1] && ban[i * 3 + 2] ) {
printf("-1\n");
return 0;
}
memset(col, -1, sizeof col);
fixXPos(1);
printf("-1\n");
return 0;
}
点击展开/折叠 vegetables.cpp
/*Lucky_Glass*/
#include <cstdio>
#include <cstring>
#include <algorithm> #define con(typ) const typ &
typedef long long llong;
const int N = 1e5 + 10; inline int rin(int &r) {
int b = 1, c = getchar(); r = 0;
while ( c < '0' || '9' < c ) b = c == '-' ? -1 : b, c = getchar();
while ( '0' <= c && c <= '9' ) r = r * 10 + (c ^ '0'), c = getchar();
return r *= b;
} struct Item {
int val, cnt, bad;
Item() {}
Item(con(int) _val, con(int) _cnt, con(int) _bad)
: val(_val), cnt(_cnt), bad(_bad) {}
static bool cmpToVal(con(Item) p, con(Item) q) {
return p.val > q.val;
}
} itm[N << 1]; struct Dsu {
int fa[N];
int fFind(con(int) u) {return fa[u] == u ? u : fa[u] = fFind(fa[u]);}
bool mMerge(int u, int v) {
u = fFind(u), v = fFind(v);
if ( u == v ) return false;
fa[u] = v;
return true;
}
void init(con(int) siz) {
for (int i = 1; i <= siz; ++i)
fa[i] = i;
}
} dsu; int n, m, nq, nitm;
int ocnt[N];
llong ans[N * 10]; int main() {
rin(n), rin(m), rin(nq);
const int ED = N - 3;
for (int i = 1; i <= n; ++i) {
int per, ext, cnt, bad;
rin(per), rin(ext), rin(cnt), rin(bad);
int due = bad ? (cnt + bad - 1) / bad : ED;
itm[++nitm] = Item(per + ext, 1, -due);
if ( cnt > 1 ) itm[++nitm] = Item(per, cnt - 1, bad);
}
std::sort(itm + 1, itm + 1 + nitm, Item::cmpToVal);
dsu.init(ED);
int ncnt = 0;
for (int i = 1; i <= nitm; ++i)
if ( itm[i].bad >= 0 ) {
while ( itm[i].cnt ) {
int due = itm[i].bad ? (itm[i].cnt + itm[i].bad - 1) / itm[i].bad : ED;
int k = dsu.fFind(due);
if ( !k ) break;
++ocnt[k];
if ( ocnt[k] == m ) dsu.mMerge(k, k - 1);
ans[ncnt + 1] = ans[ncnt] + itm[i].val;
++ncnt;
--itm[i].cnt;
}
} else {
int k = dsu.fFind(-itm[i].bad);
if ( k ) {
++ocnt[k];
if ( ocnt[k] == m ) dsu.mMerge(k, k - 1);
ans[ncnt + 1] = ans[ncnt] + itm[i].val;
++ncnt;
}
}
while ( nq-- ) {
int day; rin(day);
printf("%lld\n", ans[std::min(day * m, ncnt)]);
}
return 0;
}

THE END

Thanks for reading!

我也曾 隐约想过 从这世界逃离

因为人总被缚于不明所以的意义

生日飘落杏花雨 虫鸣淹没住叹息

尽数凝固成往昔连存在都无从证明

——《我也曾想过一了百了(中文填词)》 By 洛天依

> Link 我也曾想过一了百了 - Bilibili

「SOL」NOI2017Day2 T1T2的更多相关文章

  1. 一本通1648【例 1】「NOIP2011」计算系数

    1648: [例 1]「NOIP2011」计算系数 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 给定一个多项式 (ax+by)k ,请求出多项式展开后 x ...

  2. 「SCOI2016」背单词

    「SCOI2016」背单词 Lweb 面对如山的英语单词,陷入了深深的沉思,「我怎么样才能快点学完,然后去玩三国杀呢?」.这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,然后 ...

  3. loj2009. 「SCOI2015」小凸玩密室

    「SCOI2015」小凸玩密室 小凸和小方相约玩密室逃脱,这个密室是一棵有 $ n $ 个节点的完全二叉树,每个节点有一个灯泡.点亮所有灯泡即可逃出密室.每个灯泡有个权值 $ A_i $,每条边也有个 ...

  4. 「译」JUnit 5 系列:条件测试

    原文地址:http://blog.codefx.org/libraries/junit-5-conditions/ 原文日期:08, May, 2016 译文首发:Linesh 的博客:「译」JUni ...

  5. 「译」JUnit 5 系列:扩展模型(Extension Model)

    原文地址:http://blog.codefx.org/design/architecture/junit-5-extension-model/ 原文日期:11, Apr, 2016 译文首发:Lin ...

  6. JavaScript OOP 之「创建对象」

    工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程.工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题. function createPers ...

  7. 「C++」理解智能指针

    维基百科上面对于「智能指针」是这样描述的: 智能指针(英语:Smart pointer)是一种抽象的数据类型.在程序设计中,它通常是经由类型模板(class template)来实做,借由模板(tem ...

  8. 「JavaScript」四种跨域方式详解

    超详细并且带 Demo 的 JavaScript 跨域指南来了! 本文基于你了解 JavaScript 的同源策略,并且了解使用跨域跨域的理由. 1. JSONP 首先要介绍的跨域方法必然是 JSON ...

  9. 「2014-5-31」Z-Stack - Modification of Zigbee Device Object for better network access management

    写一份赏心悦目的工程文档,是很困难的事情.若想写得完善,不仅得用对工具(use the right tools),注重文笔,还得投入大把时间,真心是一件难度颇高的事情.但,若是真写好了,也是善莫大焉: ...

  10. 「2014-3-18」multi-pattern string match using aho-corasick

    我是擅(倾)长(向)把一篇文章写成杂文的.毕竟,写博客记录生活点滴,比不得发 paper,要求字斟句酌八股结构到位:风格偏杂文一点,也是没人拒稿的.这么说来,arxiv 就好比是 paper 世界的博 ...

随机推荐

  1. NXOpen获取UFUN的tag

    #include <NXOpen/NXObject.hxx>#include <NXOpen/NXObjectManager.hxx> 1 NXObject* nXObject ...

  2. Android 切换wifi小记

    我手机是Android 7.1的魅族.相关网络权限注册之后,还有一个特别的权限就是<uses-permission android:name="android.permission.A ...

  3. .net core 添加省市区三级联动以及编辑时显示选中的城市。

    1 @model Core.Net.Model.CoreNetBuild.CoreNetPrejectAllocation; 2 @using Core.Net.Common.Core.Net.Cor ...

  4. JAVA查漏补缺 1

    JAVA查漏补缺 1 目录 JAVA查漏补缺 1 基本数据类型 数组 方法参数传递机制 基本数据类型 数据类型 关键字 取值范围 内存占用(字节数) 整型 byte -128~127 1 整型 sho ...

  5. nodejs mongoose连接mongodb报错,command find requires authentication

    MongoError: command find requires authentication at Connection.<anonymous> (/home/Map/node_mod ...

  6. slitaz5安装vim,sakura终端命令行打不开

    刚开始安装了vim后,vim提示libtinof.so.6打不开.在网上查,发现可能是库缺少.然后查看了依赖库文件 ldd /usr/bin/vim 发现果然缺少了 libncurses.so.6 的 ...

  7. 2023-03-02 记录一下关于chatGPT使用方法

    国内版: 在线免费web版: https://chat.forchange.cn/(不用登录) https://app.writesonic.com/login(要登录) 在线免费微信版:AI对话未来 ...

  8. ider git Reset Type 使用记录

    Soft:在选定提交点之后所做的所有更改都将被暂存(这意味着可以到 Version Control 窗口(Alt+9)的Local Changes 选项卡,以便您可以查看它们,并在必要时稍后提交). ...

  9. vue2 element-ui组件二封-表单组件-按钮封装

    这里是一段我们公司过往项目的代码(增删改查项目中的查询/重置按钮) <el-button @click="query()" type="primary" ...

  10. RayLink 远控软件又推出 2 个重磅宝藏功能免费用

    你有没有在远程办公时,担心他人偷窥电脑?以致于保密性资料或私密信息,遭到泄露.创意被剽窃......又或是遇到过邻座同事屏幕前明明没人,鼠标箭头却自个浏览起网页的惊悚画面? 如果你有上述情况,建议使用 ...