AcWing提高算法课Level-3 第六章 基础算法
位运算
AcWing 90. 64位整数乘法761人打卡
递推与递归
AcWing 95. 费解的开关520人打卡
AcWing 97. 约数之和379人打卡
AcWing 98. 分形之城268人打卡
前缀和与差分
AcWing 99. 激光炸弹460人打卡
AcWing 100. 增减序列407人打卡
二分
AcWing 102. 最佳牛围栏423人打卡
AcWing 113. 特殊排序280人打卡
排序
AcWing 105. 七夕祭262人打卡
AcWing 106. 动态中位数326人打卡
AcWing 107. 超快速排序328人打卡
RMQ
AcWing 1273. 天才的记忆
代码
AcWing 90. 64位整数乘法761人
//将b的每一位拆成二进制与a相应的幂相乘
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100010;
int main(){
LL a, b, p, ans=0;
cin>>a>>b>>p;
while(b){
if(b&1)ans = (ans+a)%p;
b >>= 1;
a = a*2%p;
}
cout<<ans<<"\n";
return 0;
}
AcWing 95. 费解的开关520人
//题意:给出一组5*5的开关,每次可以改变一个点和它上下左右的开关,判断能否6步内让所有灯点亮,输出步数。
//思路:可以发现性质,如果确定了第i行的方案的话,那么后面的行数都可以依此递推,因此只需要枚举第一行即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int a[10][10], b[10][10], ans;
void change(int x, int y){
b[x][y] ^= 1;
b[x-1][y]^=1;
b[x][y+1]^=1;
b[x][y-1]^=1;
b[x+1][y]^=1;
}
int check(){
for(int i = 1; i <= 5; i++)
for(int j = 1; j <= 5; j++)
if(!b[i][j])return false;
return true;
}
int calc(){//求最小步数
ans = 1e9+10;
for(int i = 0; i < (1<<5); i++){
int tmp = 0;
memcpy(b, a, sizeof(a));
for(int j = 1; j <= 5; j++){//枚举第1行改变哪几个
if(i>>(j-1) & 1){
change(1,j);
tmp++;
}
}
for(int j = 1; j <= 4; j++){//按照现在的第一行计算
for(int k = 1; k <= 5; k++){
if(!b[j][k]){
change(j+1,k);
tmp++;
}
}
}
if(check())ans = min(ans, tmp);
}
return ans;
}
int main(){
int T; cin>>T;
while(T--){
getchar();
for(int i = 1; i <= 5; i++){
for(int j = 1; j <= 5; j++){
a[i][j] = getchar()-'0';
}
getchar();
}
if(calc()<=6){
cout<<ans<<"\n";
}else{
cout<<"-1\n";
}
}
return 0;
}
AcWing 97. 约数之和379人
//题意:求出A^B的所有约数和膜9901的值,A,B<5e7
//思路:约数之和(p1^0+p1^1+…+p1^c1)*...*(pk^0+pk^1+…+pk^ck), B的所有约数和膜9901的值,B次方意味着c1,c2,都乘以B
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 9901;
LL mpow(LL a, LL x) {
if(x==0)return 1;
LL t = mpow(a, x>>1);
if(x%2==0)return t*t%mod;
return t*t%mod*a%mod;
}
LL sum(int p, int c){//sum=p1^0+p1^1+…+p1^c1
if(c==0){
return 1;
}else if(c%2==1){
return (1+mpow(p,c/2+1))*sum(p,c/2)%mod;
}else{
return (p%mod*sum(p,c-1)+1)%mod;
}
}
int main(){
int a, b; cin>>a>>b;
if(a==0){cout<<"0\n"; return 0;}
int ans = 1;
for(int i = 2; i <= a; i++){
int s = 0;
while(a%i==0){//pi^ci==i^s
s++;
a/=i;
}
if(s)ans = ans*sum(i,s*b)%mod;//sum=(sum*p+1)%mod;
}
cout<<ans<<"\n";
return 0;
}
AcWing 98. 分形之城268人
//题意:等级n的城市有2^2n个街区,按照图中的方式旋转相连,求编号a,b的街区之间的距离。
//思路:可以发现规律,左上角为顺时针旋转90即(x,y)变位(y,x)。左下角逆时针旋转90且水平翻转,即变为(-y,-x)。右上角和右下角没有旋转,只是平移。知道转移之后递归计算。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PLL;
PLL calc(LL n, LL m){
if(n==0)return make_pair(0,0);
LL len = 1LL<<(n-1), cnt = 1LL<<(2*n-2);//n-1级图的边长和街区数
PLL pos = calc(n-1,m%cnt);//递归计算n-1级时的坐标
LL x = pos.first, y = pos.second;
LL z = m/cnt;//判断所在区域并推出当前坐标
if(z==0)return make_pair(y,x);
if(z==1)return make_pair(x,y+len);
if(z==2)return make_pair(x+len,y+len);
return make_pair(2*len-1-y,len-1-x);
}
int main(){
int T; cin>>T;
while(T--){
LL n, a, b; cin>>n>>a>>b;
PLL x = calc(n,a-1);
PLL y = calc(n,b-1);
LL dx=x.first-y.first, dy=x.second-y.second;
printf("%.0lf\n", sqrt(dx*dx+dy*dy)*10);
}
return 0;
}
AcWing 99. 激光炸弹460人
//题意:给出n个点的坐标和价值wi,求边长为R的正方形最多可以覆盖的价值
//思路:维护二维前缀和,扫一遍统计即可
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5050;
int s[maxn][maxn];
int main(){
int n, r; cin>>n>>r;
r = min(r, 5010);
for(int i = 1; i <= n; i++){
int x, y, w; cin>>x>>y>>w;
s[++x][++y] += w;//WA, 边界
}
for(int i = 1; i <= 5010; i++){
for(int j = 1; j <= 5010; j++){
s[i][j] += s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
}
int ans = -1e9+10;
for(int i = 1; i <= 5010; i++){
for(int j = 1; j <= 5010; j++){
int x1=i,y1=j, x2=min(5010,i+r-1), y2=min(5010,j+r-1);
ans = max(ans, s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);
}
}
cout<<ans<<"\n";
return 0;
}
AcWing 100. 增减序列407人
//题意:给出长为n的数列,每次可以对[l,r]+1或-1,求最少多少次让所有数都一样,且最少次数下可能的数列有几种。
//思路:
//+ 让序列相同,等价于让差分序列[2,n]全为0,b1有几种,答案就有几种。
//+ 每次可以修改b[l]+=d,b[r+1]-=d,因为不关注方案只要结果而且一定能恰好。所以贪心的每次选bi(>0)和bj(<0)配对,负数增加,正数减少,可以最快地到达全为0的状态,无法配对的和b1配,
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
LL a[maxn], c[maxn];
int main(){
LL n; cin>>n;
for(int i = 1; i <= n; i++){
cin>>a[i];
}
LL z=0, f=0;
for(int i = 2; i <= n; i++){
c[i] = a[i]-a[i-1];
if(c[i]>0)z += c[i];
else f -= c[i];
}
cout<<max(z,f)<<"\n"<<abs(z-f)+1<<"\n";
return 0;
}
AcWing 102. 最佳牛围栏423人
//题意:n块田分别有ai头牛,选择其中某一连续大于等于F块地的部分,令其中牛的总数的平均值最大,求最大平均值*1000。
//题意:n个数,求所有连续且满足长度>=F的子序列的平均值的最大值
//思路:最优化问题,想到二分平均值,判断是否可行。判断时都减去avg,若区间前缀和>=0则成立,因为长度不定,所以扫描区间时用双指针。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
int n, f, a[maxn]; double s[maxn];
bool check(double avg){
for(int i = 1; i <= n; i++)
s[i] = s[i-1]+a[i]-avg;
double mi = 0;
int l = 0, r = f;
while(r <= n){
mi = min(mi, s[l]);
if(s[r]-mi >= 0)return true;
l++; r++;
}
return false;
}
int main(){
cin>>n>>f;
int mx = 0;
for(int i = 1; i <= n; i++){
cin>>a[i]; mx = max(mx, a[i]);
}
double l = 0, r = mx;
while(r-l >= 1e-5){
double mid = (l+r)/2;
if(check(mid))l = mid;
else r = mid;
}
cout<<(int)(r*1000)<<"\n";
return 0;
}
AcWing 113. 特殊排序280人
//题意:n个元素(1-1000),任意两个之间的大小关系确定(提供交互查询函数),不存在元素相等,通过不超过10000比较对数组进行排序。
//思路:归并排序满足"元素大小关系不具传递性",直接sort。
// Forward declaration of compare API.
// bool compare(int a, int b);
// return bool means whether a is less than b.
class Solution {
public:
vector<int> specialSort(int N) {
vector<int>vc;
for(int i = 1; i <= N; i++)
vc.push_back(i);
stable_sort(vc.begin(), vc.end(), compare);
return vc;
}
};
AcWing 105. 七夕祭262人
//题意:n*m个摊点,其中T个感兴趣。求是否能通过交换相邻点使各行各列感兴趣的摊点一样多,如果能,输出最少交换次数。
//思路:可以发现性质,每次交换任意相邻两数,只会改变一行或一列的摊数,而不会同时改变行与列,因此可以行列分开做,成为纸牌均分问题。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100010;
int n, m, t;
int b[maxn], c[maxn], s[maxn];
LL calc(int a[], int n){
for(int i = 1; i <= n; i++){
a[i] -= a[0]/n;
s[i] = s[i-1]+a[i];
}
sort(s+1,s+n+1);
int ans = 0;
for(int i = 1; i <= n; i++)ans += abs(s[i]-s[n+1>>1]);
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m>>t;
for(int i = 1; i <= t; i++){
int x, y; cin>>x>>y;
b[x]++, c[y]++;
}
for(int i = 1; i <= n; i++)b[0]+=b[i];
for(int i = 1; i <= m; i++)c[0]+=c[i];
if(b[0]%n==0 && c[0]%m==0){
cout<<"both "<<calc(b,n)+calc(c,m)<<"\n";
}else if(b[0]%n==0){
cout<<"row "<<calc(b,n)<<"\n";
}else if(c[0]%m==0){
cout<<"column "<<calc(c,m)<<"\n";
}else{
cout<<"impossible\n";
}
return 0;
}
AcWing 106. 动态中位数326人
//题意:给出n个数,输出每奇数个时(从1到1,3,5的子序列)的中位数
//思路:大根堆q1维护比当前中位数小的元素,小跟堆q2维护比当前中位数大的元素,我们把中位数放在大根堆q1的堆顶,于是有:输入的数为奇数个时:q1.size()=q2.size()+1 (多一个中位数)输入的数为偶数个时:q1.size()=q2.size() (不存在中位数)。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
priority_queue<int>q1;//da
priority_queue<int,vector<int>,greater<int> >q2;//xiao
int main(){
int T; cin>>T;
for(int _w = 1; _w <= T; _w++){
while(q1.size())q1.pop();
while(q2.size())q2.pop();
int t,n; cin>>t>>n;
cout<<_w<<" "<<(n+1)/2<<"\n";
for(int i = 1; i <= n; i++){
int x; cin>>x;
if(q1.empty())q1.push(x);
else{
if(x>q1.top())q2.push(x);
else q1.push(x);
}
while(q1.size()<q2.size()){
q1.push(q2.top()); q2.pop();
}
while(q1.size()>q2.size()+1){
q2.push(q1.top()); q1.pop();
}
if(i&1)cout<<q1.top()<<" ";
if(!(i%20))cout<<"\n";//10个换行
}
cout<<"\n";
}
return 0;
}
AcWing 107. 超快速排序328人
//题意:给出n个数,每次交换两个数直到升序,求交换次数。
//思路:即冒泡排序交换次数,即逆序对个数。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 500010;
int a[maxn]; long long ans = 0;
void MergeSort(int l, int r){
if(l >= r)return ;
int m = l+r>>1;
MergeSort(l,m);
MergeSort(m+1,r);
int i = l, j = m+1;
int t[r-l+1], k = 0;
while(i<=m && j<=r){
if(a[i]<=a[j])t[k++]=a[i++];
else{
t[k++] = a[j++];
ans += m-i+1;//加上剩余元素个数
}
}
while(i<=m)t[k++]=a[i++];
while(j<=r)t[k++]=a[j++];
for(i=l, k=0; i <= r; i++,k++)a[i]=t[k];
}
int main(){
int n;
while(cin>>n &&n){
ans = 0;
for(int i = 1; i <= n; i++)cin>>a[i];
MergeSort(1,n);
cout<<ans<<"\n";
}
return 0;
}
AcWing 1273. 天才的记忆
//题意:RMQ,给出长为n的序列,m个询问,区间[l,r]内的最大值
//思路:线段树
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 200010;
int a[maxn];
struct node{
int l, r;
int val;
}sgt[maxn<<2];
void build(int p, int l, int r){
sgt[p].l = l, sgt[p].r = r;
if(l == r){
sgt[p].val = a[l];
}else{
int m = (l+r)/2;
build(p*2,l,m);
build(p*2+1,m+1,r);
sgt[p].val = max(sgt[p*2].val,sgt[p*2+1].val);
}
}
int query(int p, int l, int r){
if(l <= sgt[p].l && sgt[p].r <= r)return sgt[p].val;
int m = (sgt[p].l+sgt[p].r)/2, ans = -1e9+10;
if(l <= m)ans = max(ans, query(p*2,l,r));
if(r > m)ans = max(ans, query(p*2+1,l,r));
return ans;
}
int main(){
int n, m;
cin>>n;
for(int i = 1; i <= n; i++)cin>>a[i];
build(1,1,n);
cin>>m;
for(int i = 1; i <= m; i++){
int x, y; cin>>x>>y;
cout<<query(1,x,y)<<"\n";
}
return 0;
}