C.Triple Attack
一句话题意:
n个敌人,你去攻击它。当你不是3的倍数,攻击力是1,敌人下降1,当你是3的倍数,敌人下降3.
\(a_i <= 10^9\)
\(n<=10^5\)
Analysis:
又是一道周期问题的题目。
所以需要想到5是一个周期,“1,1,3”是一组,然后这一组的开头和结尾是不完整的周期。
#include<bitsstdc++.h>
#define int long long
using namespace std;
int n, last, x, cnt, fl;
signed main(){
cin>>n;
for(int i = 1; i <= n; i++){// 6 11 7
cin>>x; fl = 0;
if(last){
// cout<<"iii: "<<i<<" "<<last<<endl;
if(x >= 5 - last){
cnt += (5 - last == 4 ? 2: 1);
x -= (5 - last);
}
else{
if(last == 1){
if(x == 1){
//1
//1 3 -> 2 3 4
cnt += 1;
last = 2;
}
else {
//1
//1 3 -> 2 3 4
cnt += 2;
last = 0;
}
}
else if(last == 2){
// 1 1
// 3 -> 1 2 3
cnt += 1;
last = 0;
}
fl = 1;
}
}
// cout<<"teshu: "<<cnt<<endl;
if(fl == 1) continue;
cnt += x / 5 * 3;
last = x % 5; //整数周期后,留的尾巴,从第一个周期开始
if(last == 1){// 1
cnt += 1;
}
else if(last == 2){
cnt += 2;
}
else if(last == 3 || last == 4){
cnt += 3;
last = 0;
}
// cout<<"cnt: "<<cnt<<endl;
// cnt = 0;
}
cout<<cnt<<endl;
return 0;
}
/*
1 1 3 1 6
1 3 2
1 1 2
3
6 11 7
1 1 3 1 6(4)
1 3
1 1 3
1 1 11(7)
3
1 1 3 7(4)
*/
D. Minimum Steiner Tree
一句话题意:
有一棵树,给定几个点,求这几个点组成最小的树。
\(N <= 10^5\)
Analysis:
最近公共祖先入门的时候可以做的题目。
可以从一个点向祖先靠近,每一个点都打上标记,其他点也向祖先靠近的过程如果遇到标记就停下来,cnt++,记录这个过程中深度最浅的点,最后减掉这个深度。
#include<bits/stdc++.h>
using namespace std;
int n, m, dep[200005], hd[200005], f[200005], cnt , b[200005], tot;
struct Edge{
int to, nxt;
}edge[400005];
void add(int u, int v){
edge[++cnt].to = v;
edge[cnt].nxt = hd[u];
hd[u] = cnt;
}
void dfs(int u, int fa, int d){
// cout<<u<<" "<<fa<<" "<<d<<endl;
dep[u] = d;
f[u] = fa;
for(int i = hd[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(v == fa) continue;
dfs(v, u, d+1);
}
}
int main(){
cin>>n>>m; int x,y;
for(int i = 1; i <= n-1; i++){
cin>>x>>y;
add(x,y); add(y,x);
}
dfs(1, 0, 1);
cin>>x;
while(x != 0){//先走到根节点
b[x] = 1;
x = f[x];
tot++;
}
// cout<<"tot: "<<tot<<endl;
int mn = INT_MAX, p;
for(int i = 2; i <= m; i++){
cin>>x;
while(b[x] != 1){
b[x] = 1;
tot++;
x = f[x];
}
// cout<<"tot: "<<tot<<endl;
if(dep[x] < mn) mn = dep[x];
}
cout<<max(1, tot - mn + 1)<<endl;
return 0;
}
G:
[ABC368G] Add and Multiply Queries
给定两个长度为 $ N $ 的正整数序列 $ A, B $。需要处理 $ Q $ 个按顺序给出的查询。查询有以下三种类型:
- 类型 $ 1 $:格式为
1 i x
。将 $ A_i $ 替换为 $ x $。 - 类型 $ 2 $:格式为
2 i x
。将 $ B_i $ 替换为 $ x $。 - 类型 $ 3 $:格式为
3 l r
。需要解决以下问题并输出答案:- 初始时 $ v = 0 $。依次对 $ i = l, l + 1, \dots, r $ 进行操作,每次操作将 $ v $ 替换为 $ v + A_i $ 或 $ v \times B_i $。求最终能得到的 $ v $ 的最大值。
需要注意的是,输入中类型 $ 3 $ 的查询的答案保证在 $ 10^{18} $ 以下。
- 初始时 $ v = 0 $。依次对 $ i = l, l + 1, \dots, r $ 进行操作,每次操作将 $ v $ 替换为 $ v + A_i $ 或 $ v \times B_i $。求最终能得到的 $ v $ 的最大值。
制約
- $ 1 \leq N \leq 10^5 $
- $ 1 \leq A_i \leq 10^9 $
- $ 1 \leq B_i \leq 10^9 $
- $ 1 \leq Q \leq 10^5 $
- 类型 $ 1 $, $ 2 $ 的查询中,$ 1 \leq i \leq N $
- 类型 $ 1 $, $ 2 $ 的查询中,$ 1 \leq x \leq 10^9 $
- 类型 $ 3 $ 的查询中,$ 1 \leq l \leq r \leq N $
- 类型 $ 3 $ 的查询中,输出值在 $ 10^{18} $ 以下
类型 3 的查询的答案保证在 \(10^18\),约等于\(2^62\),也就是\(10^5\)个数字中只有60多次大于2的情况,其余情况都是1.
这样的话,我们大多数连续的区间,都是要选择A序列进行相加。
可以吧B序列中非1的位置放入set中,每次找到1的区间,树状数组求和,快速加给v。B序列单独比较非1的情况。
一开始想不明白:
1.当前询问在上一个询问基础上加减(傻X)
2.错误:没有修改:b[loc] = x;
3.错误:v = max(v+a[pos], v*b[pos]); //写成 v+= ....
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, a[100005],b [100005], c[100005], q, op, res;
set<int> s;
int lowbit(int x){
return x & (-x);
}
void update(int i, int x){
while(i <= 100005){//
c[i] += x, i += lowbit(i);
}
}
int sum(int i){//前缀和
int ans = 0;
while(i){
ans += c[i]; i -= lowbit(i);
}
return ans;
}
int loc, x;
signed main(){
cin>>n;
for(int i = 1; i <= n; i++){
cin>>a[i]; update(i, a[i]);
}
for(int i = 1; i <= n; i++){
cin>>b[i];
if(b[i] > 1) s.insert(i);
}
//
s.insert(n);
cin>>q;
for(int i = 1; i <= q; i++){
cin>>op>>loc>>x;
if(op == 1){
update(loc, x - a[loc]);
a[loc] = x;
}
else if(op == 2){
if(b[loc] == 1){
if(x != 1) s.insert(loc);
}
else{
if(x == 1) s.erase(loc);
}
b[loc] = x;
}
else{
int v = a[loc++], r = x;//第一个数字一定是加到v中
set<int>::iterator it = s.lower_bound(loc);
while(loc <= r){
int pos = *it;
v += sum(min(pos-1, r)) - sum(loc-1);
if(pos > r) break;
v = max(v+a[pos], v*b[pos]); //写成 v+= ....
loc = pos+1;
it++;
}
cout<<v<<endl;
}
}
return 0;
}