B
- 这是一道很好的线段树练习题,思路很清晰,查询区间进制最小值所对应的进制显然应该是区间最大值+1,所以使用一个线段树维护区间最大值;那么如何维护区间进制呢?让另外一颗线段树记录数字信息即可
- 之前没做过区间进制的问题,记录一下
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 100;
const int MOD = 1e9 + 7;
string s;
struct Tree_Max{
int l;
int r;
int MAX;
}tr_max[N << 2];
void Push_UP_Max(int u){
tr_max[u].MAX = max(tr_max[u << 1].MAX, tr_max[u << 1 | 1].MAX);
}
void Build_Max_Tree(int u, int l, int r){
if(l == r){
tr_max[u] = {l, r, s[l] - '0'};
}else{
tr_max[u] = {l, r};
int mid = (r - l >> 1) + l;
Build_Max_Tree(u << 1, l, mid);
Build_Max_Tree(u << 1 | 1, mid + 1, r);
Push_UP_Max(u);
}
}
void modify_Max(int u, int x, int y){
if(tr_max[u].l == tr_max[u].r){
tr_max[u].MAX = y;
}else{
int mid = (tr_max[u].l + tr_max[u].r >> 1);
if(x <= mid){
modify_Max(u << 1, x, y);
}else{
modify_Max(u << 1 | 1, x, y);
}
Push_UP_Max(u);
}
}
int query_max(int u, int l, int r){
if(tr_max[u].l >= l && tr_max[u].r <= r){
return tr_max[u].MAX;
}else{
int mid = (tr_max[u].l + tr_max[u].r >> 1);
int ans = -1;
if(l <= mid){
ans = max(ans, query_max(u << 1, l, r));
}
if(mid < r){
ans = max(ans, query_max(u << 1 | 1, l, r));
}
return ans;
}
}
int fastpow(ll base, int power){//注意爆ll,以后快速幂就统一long long
ll ans = 1;
while(power > 0){
if(power & 1) ans = ans * base % MOD;
base = base * base % MOD;
power >>= 1;
}
return (int)ans;
}
struct Tree_Base{
int l;
int r;
int val;
}tr_Base[N << 2][11];// 2 - 10进制
void Push_Up_Base(int u, int k){
int sz = tr_Base[u << 1 | 1][k].r - tr_Base[u << 1 | 1][k].l + 1;//注意这里是右子树
tr_Base[u][k].val = (1ll * tr_Base[u << 1][k].val * fastpow(k, sz) % MOD + tr_Base[u << 1 | 1][k].val) % MOD;
}
void Build_Base_Tree(int u, int l, int r, int k){//建立k进制的线段树
if(l == r){
tr_Base[u][k] = {l, r, s[l] - '0'};
}else{
tr_Base[u][k] = {l, r};
int mid = (r - l >> 1) + l;
Build_Base_Tree(u << 1, l, mid, k);
Build_Base_Tree(u << 1 | 1, mid + 1, r, k);
Push_Up_Base(u, k);
}
}
void modify_Base(int u, int x, int y, int k){
if(tr_Base[u][k].l == tr_Base[u][k].r){
tr_Base[u][k].val = y;
}else{
int mid = (tr_Base[u][k].l + tr_Base[u][k].r >> 1);
if(x <= mid) modify_Base(u << 1, x, y, k);
else modify_Base(u << 1 | 1, x, y, k);
Push_Up_Base(u, k);
}
}
int query_Base(int u, int l, int r, int k){
if(tr_Base[u][k].l >= l && tr_Base[u][k].r <= r){
return tr_Base[u][k].val;
}else{
int mid = tr_Base[u][k].l + tr_Base[u][k].r >> 1;
ll ans = 0;
if(l <= mid){
ans += query_Base(u << 1, l, r, k);
}
ans *= fastpow(k, min(r, tr_Base[u][k].r) - mid);// !!!注意这里范围
ans %= MOD;
if(mid < r){
ans += query_Base(u << 1 | 1, l, r, k);
}
ans %= MOD;
return (int)ans;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
s.resize(n + 1);
for(int i=1;i<=n;i++) cin >> s[i];
Build_Max_Tree(1, 1, n);
for(int i=2;i<=10;i++){
Build_Base_Tree(1, 1, n, i);
}
while(q--){
int opt, x, y;
cin >> opt >> x >> y;
if(opt == 1){
modify_Max(1, x, y);
for(int i=2;i<=10;i++){
modify_Base(1, x, y, i);
}
}else{
cout << query_Base(1, x, y, max(2, query_max(1, x, y) + 1)) << '\n';
}
}
return 0;
}
G
定义子序列权值为子序列最小值和最大值的乘积,问一个数组中所有非空子序列的权值乘积是多少,取模
- 理解题解中所讲的一个长度为 k k k的区间,中间的数对答案的贡献是 2 k − 2 2^{k-2} 2k−2,但是此题有更好的办法
- 考虑枚举每一个数,当然首先把它们从小到大排序,设这个数是第 i i i个, i i i从0开始,那么因为已经排好序了,那么它能够对答案造成多少贡献呢?因为贡献一定是这个数是子序列里面的最大值或者是最小值,如果成立为最小值,那么子序列的其他数一定是在这个数后面的某些数或者没有,它后面一共有 2 n − i − 1 2^{n-i-1} 2n−i−1个数,那么根据一个长度为 n n n的数组的子序列个数一共是 2 n 2^{n} 2n(包括空序列),所以这个时候这个数对答案的贡献是 2 n − i − 1 2^{n-i-1} 2n−i−1;同理可得当这个数成立为最大值的时候,对答案的贡献是 2 i 2^{i} 2i,所以答案应该是 ∑ i = 0 n − 1 a [ i ] 2 i + 2 n − i − 1 \sum_{i=0}^{n-1} a[i]^{2^{i}+2^{n-i-1}} ∑i=0n−1a[i]2i+2n−i−1,当然首先要欧拉降幂降指数,因为模数是一个质数,所以欧拉函数值就是 M O D − 1 MOD-1 MOD−1
- 看jiangly代码突然悟到了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
ll fastpow(ll base, ll power, ll MOD){
ll ans = 1;
while(power){
if(power & 1) ans = ans * base % MOD;
base = base * base % MOD;
power >>= 1;
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<ll> POW(n);
POW[0] = 1ll;
for(int i=1;i<n;i++){
POW[i] = POW[i - 1] * 2 % (MOD - 1);
}
ll ans = 1;
vector<int> a(n);
for(auto &i : a) cin >> i;
sort(a.begin(), a.end());
for(int i=0;i<n;i++){
ans *= fastpow(a[i], POW[i] + POW[n - i - 1], MOD);
ans %= MOD;
}
cout << ans;
return 0;
}