题目链接
题意
小A有
个本子,现在他要再买
本来使的
,现在商店里有三种零售模式,花
元买一本,花
元买两本,话
元买3本。问最少花费多少可以满足要求。
思路
思路要清晰才行。
现在想最少差多少本满足要求呢?
代表除4的余数,那么
代表着最少差多少本可以满足要求。
若
,意味着
,花费为0.
若
,差3本,可以花
元买3本,也可以买3个1本,即
,或者买一个一本和一个两本,即
。除此之外没有别的买法了,去
即可。
若
,差两本,可以买一个两本,花费
,也可以花费
,这是很显然的,然后也可以买6本,因为
,这六本可以有很多种构成方式,但是最优的是两个三本,因为如果再加上加1本或者两本的情况都没有前面的情况优,即花费
若
,这里面有种情况可能会忽略。
可以花费
也可以买5本,因为
,花费
,还可以买9本,花费
综上
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
#define ll long long
const ll N = 2e5+10;
int main(){
ll n,a,b,c;
cin >> n >> a >> b >>c;
if(n % 4 == 0) return cout << 0,0;
ll d = 4 - n % 4;
if(d == 1) {
ll q = a;
ll w = 3 * c;
ll e = b + c;
cout << min(q,min(w,e));
}
else if(d == 2){
ll q = b;
ll w = 2 * a;
ll e = 2 * c;
cout<<min(q,min(w,e));
}
else {
ll q = c;
ll w = 3 * a;
ll e = a + b;
cout<<min(q,min(w,e));
}
}
题意
小h的妈妈给她n盆花,每朵花都有一个喜悦值,然后小h的妈妈给她m个区间,然后让小h任意选择使得小h获得的喜悦值值正。
思路
这个题意的解释有误导作用。如果深入思考会发现,其实没选择一个区间其实增加的就是这个区间的区间和。所以我们选择区间和为正的区间累加即可。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
#define ll long long
const ll N = 2e5+10;
int ans[1000];
int main(){
int n,m;
cin >> n >> m;
for(int i = 1;i <= n;++i) cin >> ans[i];
ll sum = 0;
for(int i = 1;i <= m;++i){
ll l,r;
cin >> l >> r;
ll num = 0;
for(int j = l;j <= r;++j) num += ans[j];
num = max(num,0LL);
sum += num;
}
cout<<sum;
}
这个区间有n个数字,然后有m个字区间,定义每个子区间mex为没有出现在区间中的最小的非负整数。现在让你构造出这个数组,并且尽量使得所有子区间的最小的mex值最大。
思路:
比较好想的是满足要求的mex值就是最小区间的长度,如何构造这个数组是不大好想的。我们可以这样想,我们先满足最小区间条件成立,假如最小的区间长度为
,那么这个区间的数值可以为
。我们要数组中所有区间为
的区间都满足,那么这m个区间也肯定满足,最后构造出数组方法为
,保证m个区间任意一个区间都是mex就足够了。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
const ll N = 2e5+10;
int main(){
int n,m;
cin >>n >>m;
int d = INF;
for(int i = 1;i <= m;++i){
int l,r;
cin >> l >> r;
d = min(d,r-l);
}
d ++;
cout<<d<<endl;
int cnt = 0;
for(int i = 1;i <=n;++i){
cout<<(cnt++) % d<<' ';
}
}
D
题意
一棵树,树上有
个点,标号为1~n,且1是树根。然后每个点都有一个值
,每条边也有一个权值
,然后让你输出每个可以控制点的数目。(比如
控制
,即树上
的权值
)
思路
首先比较好想的是暴力的方法。
预处理深度遍历树根到所有节点的权值,然后对每个点进行
,结果T。
我们在脑海中想象下遍历这棵树的过程,在DFS过程中在栈中总会呈现一条树根 到 子节点的路径,我们在这条路径上进行二分查找这条路径上 满足条件的 距当前节点最远点(也就是最靠根的节点),然后这一段路径区间都可以加1,区间修改,我们可以差分来缩减时间复杂度,只不过这是在树上差分。
有一点需要注意,差分时注意区间首尾别颠倒!
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
const ll N = 2e5+10;
ll d[N];//到根节点的距离
ll a[N];
int head[N],tot;
struct Edge{
int next;
int to;
ll dis;
}edge[N<<2];
inline void add(int from,int to,ll dis){
edge[++tot].next = head[from];
edge[tot].to = to;
edge[tot].dis = dis;
head[from] = tot;
}
vector<int> T;//记录路径
int c[N];//答案数组
int pre[N];//记录前驱
void dfs(int root){
int l = 0,r = T.size() - 1;
while(l < r){
int mid = l + r >> 1;
if(d[root] - d[T[mid]] <= a[root]) r = mid;
else l = mid + 1;
}
if(T.size() != 0&&d[root] - d[T[r]] <= a[root]){
c[pre[root]] ++;
c[pre[T[r]]] --;
}
T.push_back(root);
for(int i = head[root];~i;i = edge[i].next){
int y = edge[i].to;
ll dis = edge[i].dis;
d[y] = d[root] + dis;
dfs(y);
}
T.pop_back();
}
void DFS(int x){
for(int i = head[x];~i;i = edge[i].next){
int y = edge[i].to;
DFS(y);
c[x] += c[y];
}
}
int main(){
memset(head,-1,sizeof head);
int n;
scanf("%d",&n);
for(int i = 1;i <= n;++i) scanf("%lld",&a[i]);
int u;ll dis;
for(int i = 2;i <= n;++i){
scanf("%d%lld",&u,&dis);
add(u,i,dis);
pre[i] = u;
//add(i,u,dis);
}
dfs(1);
DFS(1);
for(int i = 1;i <= n;++i) cout<<c[i]<<' ';
}