HDU-2836 Traversal(树状数组+离散化+二分)
题意:给一个长为N的序列和一个数字H,问有多少的子序列(可以不连续),满足相邻数之差不超过H。N<1e5, H<1e8
题解:可以想到dp,dp[i]表示以a[i]结尾的序列的方案数,那么转移为
dp[i] = sum(dp[j])+1 其中j<i, a[i]-H<=a[j]<=a[i]+H
但是需要优化复杂度,对于sum而言很容易想到用树状数组求前缀和(线段是tle了?)
对于每个a[i]可以求得a[j]的上界和下界,然后树状数组求和sum(dp[a[j]]),需要离散化。然后更新dp[a[i]]。
另外这道题的取模好像有点问题没搞懂
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 9901
using namespace std;
int N, H;
int a[100010];
int b[100010];
long long dp[100010];
long long res;
int cnt;
int c[100010*4];
int lowbit(int x){
return x&(-x);
}
void update(int i, int x){
while(i<=cnt){
c[i] = (c[i]%mod+x%mod)%mod;
i += lowbit(i);
}
}
int sum(int x){
long long sum = 0;
while(x>0){
sum += c[x];
x -= lowbit(x);
}
return sum%mod;
}
int main(){
while(scanf("%d %d", &N, &H) == 2){
res = 0;
memset(c, 0, sizeof(c));
memset(dp, 0, sizeof(dp));
for(int i = 1; i<=N; i++){
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(b+1, b+1+N);
cnt = unique(b+1, b+1+N)-(b+1);
for(int i = 1; i<=N; i++){
int l = lower_bound(b+1, b+1+cnt, a[i]-H)-b;
int r = upper_bound(b+1, b+1+cnt, a[i]+H)-b-1;
int pos = lower_bound(b+1, b+1+cnt, a[i])-b;
//cout<<l<<" "<<r<<" "<<pos<<endl;
int ans = sum(r)-sum(l-1);
ans = (ans%mod+mod)%mod;
res = (res%mod+ans)%mod;
update(pos, ans+1);
}
printf("%lld\n", res);
}
return 0;
}
HDU-2837 Calculation(指数循环节+快速幂)
题意:定义0^0=1, f(0) = 1, f(n) = (n%10)^f(n/10), 给出n,m,求f(n)%m. 2<n,m<1e9
题解:等自己推一遍这个式子再写...
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long pow_mod(long long a, long long b, long long mod){
long long sum = 1;
while(b){
if(b & 1){
sum = sum*a;
if(sum>mod){
sum = sum%mod+mod;
}
}
a = a*a;
if(a>mod){
a = a%mod+mod;
}
b>>=1;
}
return sum;
}
long long phi(long long n){
long long ans = n;
for(long long i = 2; i*i<=n; i++){
if(n%i == 0){
ans -= ans/i;
while(n%i == 0) n/=i;
}
}
if(n>1) ans -= ans/n;
return ans;
}
long long solve(long long n, long long m){
if(n<10) return n;
long long x = solve(n/10, phi(m));
return pow_mod(n%10, x, m);
}
int main(){
int T;
scanf("%d", &T);
while(T--){
long long n, m;
scanf("%lld %lld", &n, &m);
printf("%lld\n", solve(n, m)%m);
}
return 0;
}
HDU-2838 Cow Sorting(树状数组+逆序数)
题意:给一个长为n的序列,n<1e5,每次可以交换两个元素X,Y,付出的时间代价为X+Y,求最少的交换时间使得序列能够从小到大排列。
题解:经典的树状数组求逆序数。对于某一个数X,比如他前面有a个比他大的,他就要交换a次,他后面有b个比他小的,他又要交换b次,因此他对于总的代价的贡献就是(a+b)*X, 因此就是用树状数组求逆序数a,b,然后每个数字都算一遍贡献最后加起来就行了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;
int a[100010];
int c[100010*4];
int l[100010];
int r[100010];
int lowbit(int x){
return x&(-x);
}
void update(int i, int x){
while(i<=n){
c[i] += x;
i += lowbit(i);
}
}
int sum(int x){
long long sum = 0;
while(x>0){
sum += c[x];
x -= lowbit(x);
}
return sum;
}
int main(){
scanf("%d", &n);
for(int i = 1; i<=n; i++){
scanf("%d", &a[i]);
}
for(int i = 1; i<=n; i++){
l[a[i]] = sum(n)-sum(a[i]-1);
update(a[i], 1);
}
memset(c, 0, sizeof(c));
for(int i = n; i>=1; i--){
r[a[i]] = sum(a[i]);
update(a[i], 1);
}
long long res = 0;
for(int i = 1; i<=n; i++){
res += (long long)a[i]*(l[a[i]]+r[a[i]]);
}
/*for(int i = 1; i<=n; i++){
cout<<a[i]<<" "<<l[a[i]]<<" "<<r[a[i]]<<endl;
}*/
printf("%lld\n", res);
return 0;
}