10.25集训解题报告
T1 方(\(square\))
题面:
上帝说: “要有方”。
ZAY说: “要有六方”。
二人争论不休,于是ZAY想要建立一个六方世界,来证明六方才是人间正道。
ZAY的六方世界是这样的
上帝说: “要有方”。
ZAY说: “要有六方”。
二人争论不休,于是ZAY想要建立一个六方世界,来证明六方才是人间正道。
ZAY的六方世界是这样的
在富饶的ZAY王国一共居住着N户家庭,每家每户都生活在一个六边形的方块中,由于ZAY想让王国
里的人互相交流,所以N户家庭形成一个连通块。为了防止非法移民,ZAY决定用一堵墙将所有的家庭围
起来。
ZAY制定了如下规则
- 墙在方块里
- 墙不能在家庭里
- 任何家庭不能在墙外
- 墙内可以有空白方块
定义围墙长度为所占方块数,请你设计家庭以及围墙方案,使得围墙总长最短。
思路:
找规律,一层一层向外拓展, 贡献每隔 \(n\) 个 变一次,特殊的,每一圈的第一个一定变,最后一个一定不变。
然后模拟即可。
代码:
ll n;
ll ans;
ll sum=6;
ll wz=1;
ll js=1;
int sta[10];
int Ans[7]={0,6,8,9,10,11,12};
int main(){
n=read();
if(n<=6) {cout<<Ans[n]; return 0;}
while(wz+sum<=n) {
wz+=sum; sum+=6; ++js;
}
sta[0]=wz;
for(int i=1; i<=6; ++i) {
sta[i]=wz+i*(sum/6);
}
int k=lower_bound(sta, sta+7, n)-sta;
ans=sum+k;
if(sta[k]!=n) ans--;
if(wz<n) ans++;
cout<<ans;
}
T2 肥宅快乐机(\(dundundun\))
题面:
售货机里一共有 \(n\) 个物品,每个物品 \(a_i\) 个,自然 还有 \(n\) 个购买按钮。
正常情况下,按下第 \(i\) 个按钮,需要支付 \(c_i\) 的钱,然后就会跳出一个物品 \(i\)。如果这个物品卖完了,按下按钮无效。
但是这台售货机的电路连接出了问题,第 \(i\) 个按钮的弹出电路连向了物品 \(f_i\)。
假设按下了第 \(i\) 个按钮,售货机的执行逻辑如下:
- 判断第 \(i\) 个物品是否为空。
- 如果是,不执行任何操作,退出购买程序。
- 否则要求支付 \(c_i\) 的钱。
- 因为电路坏了,实际会弹出物品 \(f_i\)。
ZAY凭借高超的运算能力计算出来 \(f_i\) 的值,并且上网查询了每种物品的卖出价格 \(d_i\),ZAY有无限本金,机制的他想通过买入和卖出最大化得到的钱。
思路:
一堆基环树。
不难发现,直接连贡献最大的边,最后整张图只有链和环。
如果是链,除了叶子都有贡献。
如果是环,那么断掉贡献最小的,还有就是次大值也会产生贡献。
代码:
struct node {
ll f, c, d, a;
}sz[100005];
int n;
int t[100005];
int t2[100005];
ll ans;
bool pd[100005];
void slove(int x) {
int v=t[x];
ll S=sz[x].d-sz[v].c;
if(v==x) {
ans+=S;
return ;
}
ll maxx=-1e18;
if(t2[x])
maxx=sz[t[x]].c-sz[t2[x]].c;
maxx=max(maxx, -S);
while(pd[v]==0&&t[v]) {
pd[v]=1;
S+=sz[v].d-sz[t[v]].c;
if(t2[v])
maxx=max(maxx, sz[t[v]].c-sz[t2[v]].c);
maxx=max(maxx, sz[t[v]].c-sz[v].d);
v=t[v];
}
if(v!=x) ans+=S;
else ans+=S+maxx;
}
int main(){
n=read();
for(int i=1; i<=n; ++i) {
sz[i].f=read(); sz[i].c=read(); sz[i].d=read(); sz[i].a=read();
}
for(int i=1; i<=n; ++i) {
if(sz[i].c>=sz[sz[i].f].d) continue ;
if(t[sz[i].f]==0) t[sz[i].f]=i;
else {
if(sz[i].c<sz[t[sz[i].f]].c) {
t2[sz[i].f]=t[sz[i].f];
t[sz[i].f]=i;
}
else if(t2[sz[i].f]==0||sz[i].c<sz[t2[sz[i].f]].c) {
t2[sz[i].f]=i;
}
}
}
for(int i=1; i<=n; ++i) {
if(t[i]==0) continue ;
ans+=1LL*(sz[i].a-1)*(sz[i].d-sz[t[i]].c);
}
for(int i=1; i<=n; ++i) {
if(t[i]==0) continue ;
if(pd[i]) continue ;
pd[i]=1;
slove(i);
}
}
T3 油炸字符串(\(string\))
题面:
ZAY赚到了大笔钱,于是想去买吃的。
字符是一种生活在草原上的优雅生物,其肉炸来鲜美无比。
于是ZAY想吃油炸字符串,但是由于串串子太难了,他遇到了麻烦。
ZAY串字符串的过程是这样的,初始时他拥有一种基础字符串 \(p\) 和一个空签子 \(S\),每次他可以选择签子的任何一个位置(最前最后都行)来插入一个 \(p\),一直不停地插直到字符串足够长。
但是悲剧的是,串完后ZAY不记得 \(p\) 长什么样了,现在给你最后的 \(S\),希望你能够还原出最初的 \(p\)。
如果有多个 \(p\) 符合要求,选择最短的。
如果有多个 \(p\) 最短,选择字典序最小的。
思路:
区间 dp。
T4 青岛地铁(\(subway\))
题面:
青岛地铁有 \(N\) 地铁站,这些地铁站从 \(1\) 到 \(N\) 编号,有 \(M\) 条地铁连接各个地铁站,这些地铁从 \(1\) 到 \(M\) 编号。第 \(i\) 条地铁是一条连接第 \(A_i\) 个和第 \(B_i\) 个地铁站的双向边,长度为 \(D_i\)。任意两个地铁站间一定有地铁(直接或间接)连接。
修缮计划如下:首先,选择一个自然数 \(X\),将和第一个地铁站距离等于 \(X\) 或在 \(X\) 以下的所有地铁站(含第一个地铁站)相互之间连接一条地下通道。地铁站 \(i\) 和地铁站 \(j\) 之间的距离指,从地铁站 \(i\) 到地铁站 \(j\) 经过的地铁的长度总和的最小值。定义 \(C\) 为一个与修筑地下通道花费有关的量(\(C\) 是整数)。
修筑所有地下通道的花费为 \(C\times X\)。
接下来,撤去已经通过地下通道连接的地铁站之间的地铁。撤去地铁的花费不计。
最后,将没有被撤去的地铁进行修补,长为 \(d\) 的地铁修补的花费为 \(d\)。
修缮计划实施之前,青岛地铁没有地下通道。请求出青岛地铁修缮花费总和的最小值。
思路:
直接 \(Dij\)。
先求出总的 \(d\) 的花费,也就是 \(X=0\) 时的花费。
然后把距离从小到大排序,枚举 \(X\)。
\(X\) 在变大,连通块内的点不会减少,只能是一个个加进来。
然后删掉往联通块内新加的边的贡献就行了。
最后对所有 \(X\) 的答案取一个最小值。
代码:
struct edge {
int t, n; ll d;
}e[400005];
int head[100005], head_size;
void ADD(int f, int t, ll d) {
e[++head_size]=(edge) {t, head[f], d};
head[f]=head_size;
}
int n, m, u, v; ll C, d;
ll dis[100005];
struct node {
int v; ll diss;
bool operator < (const node &a) const {
return a.diss<diss;
}
};
int sz[100005];
bool cmp(int a, int b) {
return dis[a]<dis[b];
}
void Dij() {
memset(dis, 0x3f, sizeof(dis));
dis[1]=0;
priority_queue<node>q;
q.push((node){1, 0});
while(q.size()) {
node op=q.top(); q.pop();
if(dis[op.v]!=op.diss) continue ;
for(int i=head[op.v]; i; i=e[i].n) {
if(dis[e[i].t]>dis[op.v]+e[i].d) {
dis[e[i].t]=dis[op.v]+e[i].d;
q.push((node){e[i].t, dis[e[i].t]});
}
}
}
}
bool pd[100005];
ll ans;
ll S;
int main(){
n=read(); m=read(); C=read();
for(int i=1; i<=m; ++i) {
u=read(); v=read(); d=read();
ADD(u, v, d); ADD(v, u, d);
ans+=d; S+=d;
}
Dij();
for(int i=1; i<=n; ++i) {
sz[i]=i;
} sort(sz+1, sz+n+1, cmp);
for(int i=1; i<=n; ++i) {
for(int j=head[sz[i]]; j; j=e[j].n) {
if(pd[e[j].t]) S-=e[j].d;
}
ans=min(ans, S+C*dis[sz[i]]);
pd[sz[i]]=1;
}
cout<<ans;
}