[每日随题7] 贪心 - 数学 - CDQ分治
整体概述
- 难度:1400 \(\rightarrow\) 2100 \(\rightarrow\) 2200
492C.Vanya and Exams
-
标签:贪心
-
前置知识:无
-
难度:Div2.C 1400
题目描述:
输入格式:
输出格式:
样例输入:
5 5 4
5 2
4 7
3 1
3 2
2 5
2 5 4
5 2
5 2
样例输出:
4
0
解题思路:
-
将 \(b_i\) 从小到大排序,每次选择使用最少的文章来提高 \(1\) 分。
-
避免浮点运算,我们可以将平均成绩乘以 \(n\) 得到所需总成绩。
-
对于每次考试,能提高成绩到 \(r\) 就提高到顶,否则就是提高到刚好为总成绩。
-
模拟一遍即可。
完整代码
#include<bits/stdc++.h>
#define Size(x) ((int)(x).size())
#define int long long
using namespace std;
const int N = 5e5+5;
int n,r,avg;
struct Node{
int a,b;
bool operator < (const Node&x) const{
return b < x.b;
}
}a[N];
inline void solve(){
cin >> n >> r >> avg;
for(int i=1;i<=n;i++) cin >> a[i].a >> a[i].b;
avg *= n;
for(int i=1;i<=n;i++) avg -= a[i].a;
sort(a+1,a+n+1);
int res = 0;
for(int i=1;i<=n;i++){
if(avg <= 0) break;
int cnt = min(avg,r-a[i].a);
res += cnt*a[i].b;
avg -= cnt;
}
cout << res;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T = 1;
while(T--) solve();
return 0;
}
852E.Casinos and travel
-
标签:数论
-
前置知识:快速幂
-
难度:Bubble Cup X - Finals.E 2100
题目描述:
输入格式:
输出格式:
样例输入:
2
1 2
3
1 2
2 3
样例输出:
4
10
解题思路:
-
题目抽象一下,给定一颗树,每个节点可以染成黑色或白色,要求任选一个点作为根节点,满足任意一条从根到叶节点的路径上黑色点的个数为偶数。
-
那么我们不难发现,每个叶节点仅会出现在一条路径上,并且无论这条路径上其他点的颜色如何选择,均可以通过修改叶节点的颜色使得整条路径上黑色节点的个数为偶数个。
-
我们先假定节点 \(1\) 为整棵树的根,设此时有 \(L\) 个叶节点,那么此时所有非叶节点均可以随意选择,总共有 $2^{n-L} 种方案。
-
当根节点只有一个孩子时,此时更改其余任意非叶节点为根时,节点 \(1\) 也变为叶节点,方案数为 \((n-L-1)*2^{n-L-1}\);更改叶节点为根时,节点 \(1\) 也变为叶节点,方案数为 \(L*2^{n-L}\)。
-
当根节点不止一个孩子时,此时更改其余任意非叶节点为根时,叶节点数量不变,方案数为 \((n-L-1)*2^{n-L}\);更改叶节点为根时,叶节点数量减 \(1\),方案数为 \(L*2^{n-L+1}\)。
-
分类讨论每种情况加起来即可,记得要勤于取模呀。
完整代码
#include<bits/stdc++.h>
#define Size(x) ((int)(x).size())
#define int long long
using namespace std;
const int N = 1e5+5,mod = 1e9+7;
int n,in[N];
inline int qpow(int a,int b){
int res = 1;
for(;b;b>>=1,a=a*a%mod)
if(b&1) res=res*a%mod;
return res;
}
inline void solve(){
cin >> n;
for(int i=1,u,v;i<n;i++){
cin >> u >> v;
++in[u],++in[v];
}
int leaf = 0, res;
for(int i=2;i<=n;i++) if(in[i] == 1) ++leaf;
if(in[1] == 1){
res = leaf*qpow(2,n-leaf)%mod;
res = (res + qpow(2,n-leaf)%mod)%mod;
res = (res + (n-leaf-1)*qpow(2,n-leaf-1)%mod)%mod;
}else{
res = leaf*qpow(2,n-leaf+1)%mod;
res = (res + (n-leaf)*qpow(2,n-leaf)%mod)%mod;
}
cout << res;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T = 1;
while(T--) solve();
return 0;
}
1045G.AI robots
-
标签:CDQ分治
-
前置知识:离散化,滑动窗口,树状数组
-
难度:Bubble Cup 11 - Finals.G 2200
题目描述:
输入格式:
输出格式:
样例输入:
3 2
3 6 1
7 3 10
10 5 8
样例输出:
1
解题思路:
-
首先我们有朴素想法,暴力枚举每一对机器人,判断是否两两在视线范围内且智商相近,但这显然是 \(O(n^2)\) 会超时。
-
接着假设我们知道对于每个机器人,有哪些机器人可以和他互相看到,那么就可以用权值树状数组快速求出智商位于自己相近的区间内有多少个机器人。可是我们发现这与逆序对不同,机器人之间要相互看到,需要同时从两个机器人处出发检测是否可以看到对方,还是需要枚举每一个机器人是否可以相互看到。
-
于是我们就想,将机器人按视野大小从大到小排序,这样只要后方的机器人可以看到前面的机器人,前面被看到的机器人就一定可以看到该机器人,解决了我们刚才的问题。
-
那么我们可以使用 \(CDQ\) 分治,先将机器人按视野从大到小排序,分治过程中按智商从小到大排序,那么在计算左跨右带来的贡献时,对于每一个右侧的机器人,我们可以通过滑动窗口确定左侧有哪些机器人的智商相近,随后将所有智商相近的机器人的位置丢进权值树状数组内,而我们又知道该机器人的视野范围可以看到哪些位置,便可以用树状数组快速查处可以看到的位置范围内有多少个智商相近的机器人了。
-
但此时我们发现还有一个问题,机器人的位置大小范围到达了 \(10^9\),开不出这么大的空间,于是我们离散化一下,记录下每个机器人位置的排名,同时再记录下每个机器人可以看到的排名范围。
-
最后记得开
long long
便大功告成了。
完整代码
#include<bits/stdc++.h>
#define Size(x) ((int)(x).size())
#define mid (((l)+(r))>>1)
#define int long long
using namespace std;
const int N = 1e5+5;
int n,m,k,b[N];
struct Robot{
int x,r,q;
int L,R; // 能看到的排名范围
}a[N];
inline void LSH(){ // 离散化
for(int i=1;i<=n;i++) b[i] = a[i].x;
sort(b,b+1+n); // 排序
m = 1;
for(int i=2;i<=n;i++){ // 去重
if(b[i] != b[m]) m++;
b[m] = b[i];
}
auto getL = [&](int x,int l=1,int r=m)->int{ // 大于等于 x 的最小值
while(l<=r) b[mid] >= x ? r = mid-1 : l = mid+1;
return l;
};
auto getR = [&](int x,int l=1,int r=m)->int{ // 小于等于 x 的最大值
while(l<=r) b[mid] <= x ? l = mid+1 : r = mid-1;
return r;
};
for(int i=1;i<=n;i++){
a[i].L = getL(a[i].x - a[i].r);
a[i].R = getR(a[i].x + a[i].r);
a[i].x = getL(a[i].x);
}
}
int tr[N];
inline void add(int x,int v){
for(int i=x;i<=n;i+=i&-i) tr[i] += v;
}
inline int sum(int x,int y){
if(y < 1 || x > y) return 0;
int res = 0;
for(int i=y;i;i&=i-1) res += tr[i];
if(x>1) for(int i=x-1;i;i&=i-1) res -= tr[i];
return res;
}
bool cmp2(const Robot&x,const Robot&y){
return x.q < y.q;
}
inline int CDQ(int l,int r){
auto merge = [&](int l,int r)->int{
int winL = l, winR = l-1, res = 0;
for(int i=mid+1;i<=r;i++){
while(winL <= mid && a[winL].q < a[i].q-k) add(a[winL++].x,-1);
while(winR + 1 <= mid && a[winR+1].q <= a[i].q+k) add(a[++winR].x,1);
res += sum(a[i].L,a[i].R);
}
for(int i=winL;i<=winR;i++) add(a[i].x,-1);
sort(a+l,a+r+1,cmp2);
return res;
};
if(l == r) return 0;
return CDQ(l,mid) + CDQ(mid+1,r) + merge(l,r);
}
bool cmp1(const Robot&x,const Robot&y){
return x.r > y.r;
}
inline void solve(){
cin >> n >> k;
for(int i=1;i<=n;i++) cin >> a[i].x >> a[i].r >> a[i].q;
sort(a+1,a+n+1,cmp1);
LSH();
cout << CDQ(1,n);
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T = 1;
while(T--) solve();
return 0;
}