A. Select Three Sticks
题目链接:Select Three Sticks
###### 题意:
给定 n n n根小木棍,长度均为正整数.你可以进行的操作是:任选一根小木棍,使其长度增加或减少 1 1 1,但保证木棍长度始终为正整数.问至少进行多少次操作,才能找到三根小木棍,使它们构成一个等边三角形.
###### 思路:
一个浅显的策略:若 a ≤ b ≤ c a\leq b\leq c a≤b≤c,则最小代价为 ∣ c − a ∣ |c-a| ∣c−a∣,因此枚举每相邻三根木棍找最小代价即可.
######
#### B. Bright, Nice, Brilliant
###### 题意
有一个 n n n层金字塔,第 i i i层有 i i i个房间,从每一层我们可以按如图箭头方向向左下或右下行走,并且我们可以在任意一个房间内放置火把。放置完火把后,从此房间开始(包括此房间)能到达的所有房间明亮值增加 1 1 1.
现规定一个 n i c e nice nice的金字塔定义如下:对于每一层,该层内的每个房间的明亮值相同.并且一个 n i c e nice nice的金字塔 n i c e nice nice值定义为每一层第一个房间明亮值之和.
现给定一正整数 n n n,要求构造一 n n n层 n i c e nice nice金字塔,使得明亮值最大.
###### 思路
神奇的 z j y zjy zjy一语道破.对于每一层两边,能到达它们的路径是唯一的,因此最大值也是固定的,在两端铺满灯即可,并且发现这样摆放刚好符合 n i c e nice nice要求.摆放示例如下:
1
11
101
1001
10001
###### code
ll T;
ll n;
bool book[520][520], tag[520][520];
int main() {
read(T);
while (T--) {
read(n);
for (R ll i=1; i<=n; i++) {
for(R ll j=1; j<=i; j++) {
if (j==1 || j==i) writesp(1);
else writesp(0);
} putchar('\n');
}
}
}
#### C. Removing Smallest Multiples
题目链接:Removing Smallest Multiples
###### 题意
有一长度为 n n n的初始集合 S = 1 , 2 , 3 , . . . , n S={1, 2, 3,...,n} S=1,2,3,...,n,以及一目标集合 T ⊂ S T\subset S T⊂S.我们可以对一集合进行操作描述如下:
选定一数值 k ∈ [ 1 , n ] k\in[1,n] k∈[1,n], 并将集合中 k k k的最小倍数删去,代价为 k k k
求进行若干次操作将集合 S S S变成 T T T的最小代价.
###### 思路:
贪心.一个显然的道理是,只有一个数 n u m num num的约数 c c c被删去了,那么原先可以删去 c c c的 k k k值,才有可能删除 n u m num num,这样可以使得删除的代价尽量小.
因此比较合适的方法是,一层循环从 1 1 1到 n n n枚举 k k k,二层循环枚举 k k k的倍数,能删即删,遇见无需删除则 b r e a k break break,因为后面的数也不可能通过 k k k来删除.
贪心复杂度 O ( n n ) \Omicron(n\sqrt n) O(nn)
###### code
const ll N=1e6+5;
ll T;
ll n;
char c[N];
bool book[N];
bool tag[N];
ll res;
int main() {
read(T);
while (T--) {
read(n);
scanf("%s", c+1);
res=0;
for (R ll i=1; i<=n; i++) {
book[i]=c[i]-'0';
tag[i]=false;
}
for (R ll i=1; i<=n; i++) {
for (R ll j=i; j<=n; j+=i) {
if (book[j]) break;
if (tag[j]) continue;
res+=i; tag[j]=true;
}
}
writeln(res);
}
}
#### D. Slime
题目链接:Slime Escape
###### 题意
在一长度为 n n n的串串上串有 n n n个 s m i l e smile smile,每个下标串一个,并且每个 s m i l e smile smile上标有一整数数值(可能为负).你作为第 k k k个 s m i l e smile smile(数值非负),每次可以向左或右移动一步,每碰到一个 s m i l e smile smile,便将其吸收,你的数值变成两 s m i l e smile smile数值之和,被吸收的 s m i l e smile smile消失.
但当你的数值变为负数时,你便会死去.
当你到达 0 0 0或 n + 1 n+1 n+1位置时,你便能逃出升天.
试问你能否活着逃出去.
###### 思路
双指针扫描。我们从起始点 k k k开始,开一指针 l l l从 k − 1 k-1 k−1向左扫描, r r r从 k + 1 k+1 k+1向右扫描,直到扫描路径数值之和为正数时(如果是负数,移动也没啥意义是吧)或到达终点时终止。然后能够移动的限定条件是当前数值必须能够承担指针移动过程中 s u m sum sum值的最小负值,最终进行若干轮扫描即可.复杂度 O ( n ) \Omicron(n) O(n)
###### code
const ll N=2e5+5;
ll T;
ll n, now;
ll num[N];
ll ls[N], rs[N], lmx[N], rmx[N];
int main() {
read(T);
while (T--) {
read(n); read(now);
for (R ll i=1; i<=n; i++) read(num[i]);
ls[0]=rs[n+1]=0; lmx[0]=rmx[n+1]=-0x7fffffff;
for (R ll i=1; i<=n; i++) ls[i]=ls[i-1]+num[i], lmx[i]=max(lmx[i-1], ls[i]);
for (R ll i=n; i; i--) rs[i]=rs[i+1]+num[i], rmx[i]=max(rmx[i+1], rs[i]);
ll l=now-1, r=now+1, ml=0x7fffffff, mr=0x7fffffff, suml=0, sumr=0, nowsum=num[now];
while (1) {
while (suml<=0 && l) {
suml+=num[l]; chkmin(ml, suml);
--l;
}
while (sumr<=0 && r<=n) {
sumr+=num[r]; chkmin(mr, sumr);
++r;
}
if ((l==0 && nowsum+ml>=0) || (r==n+1 && nowsum+mr>=0)) {
puts("YES");
break;
}
if (nowsum+ml>=0) {
nowsum+=suml;
ml=0x7fffffff; suml=0;
}
if (nowsum+mr>=0) {
nowsum+=sumr;
mr=0x7fffffff; sumr=0;
}
if (nowsum+ml<0 && nowsum+mr<0) {
puts("NO");
break;
}
}
}
}