快速幂、前缀和、差分、二分、三分
目标:高效率的日常,cf rate++ (Fighing)
三分:求凹函数和凸函数的极值问题
模板:
凸函数求极大值:
int版: (解释:若求极大值,若judge(lm)更大,即 lm 更靠近极值点(但是不确定是在左侧还是右侧),但可以保证 rm 一定在极值点右侧),保留左端点,缩右端点 r 到 rm
while(l+1<r)
{
int lm=(l+r)>>1,rm=(lm+r)>>1;
if(judge(lm)>judge(rm))
r=rm;
else
l=lm;
}
//答案取 l
double版:
while(l+eps<r)
{
double lm=(l+r)/2,rm=(lm+r)/2;
if(judge(lm)>judge(rm))
r=rm;
else
l=lm;
}
//答案取 l 或 (l+r)/2 (尽管此时 l 和 r 已经相等,但因为精度问题,取 r 可能会错)
凹函数求极小值:
int版:
while(l+1<r)
{
int lm=(l+r)>>1,rm=(lm+r)>>1;
if(judge(lm)>judge(rm))
l=lm;
else
r=rm;
}
//答案取 r
double版:
while(l+eps<r)
{
double lm=(l+r)/2,rm=(lm+r)/2;
if(judge(lm)>judge(rm))
l=lm;
else
r=rm;
}
//答案取 r 或 (l+r)/2 (尽管此时 l 和 r 已经相等,但因为精度问题,取 l 可能会错)
例题:
C -Elections
题解:(三分)
无单调性,多变量,适合三分 难点就是函数关系怎么确定
三分最终的票数,当有人比自己票数高时,从费用小的开始贿赂他的选民。剩下的从小开始买,用这个函数求得最小费用。
ps:三分的题,除了直接给函数外,真不好想
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];
char str[N];
vector<int>p[100005];
vector<int>ans;
int n,m,k,q;
int x,y,z;
int mx;
int judge(int x)
{
ans.clear();
int sum=0;
int need=x-p[0].size();//需要贿赂的最少人数
for(int i=1;i<=mx;i++)
{
for(int j=0;j<p[i].size();j++)
{
if(p[i].size()-j>=x)//不存在比自己的票多的选举者
{
need--;
sum+=p[i][j];
}
else//虽然此竞选者票数少,但是便宜
{
ans.push_back(p[i][j]);
}
}
}
if(need<=0)//满足条件的范围
return sum;
int sss=0;
sort(ans.begin(),ans.end());//找到最便宜的
while(need--)
{
sum+=ans[sss++];
}
return sum;
}
int solve(int l,int r)//三分当我被选举时的人数,使得花费 最小
{
while(l+1<r)
{
int lm=(l+r)>>1,rm=(lm+r)>>1;
if(judge(lm)>judge(rm))
l=lm;
else
r=rm;
}
return min(judge(l),judge(r));
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n;
mx=0;
for(int i=0;i<n;i++)
{
cin>>x>>y;
p[x].push_back(y);
if(mx<x)
mx=x;
}
for(int i=0;i<=mx;i++)
{
sort(p[i].begin(),p[i].end());
}
cout<<solve(p[0].size(),n);
}
B - Monitor
题解:(差分)
由于数据的量比较大,所以二维数组存不下,所以采用二维指针动态分配内存解决
其余的就是基本的差分问题了
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];
int n,m;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int x,y,x1=0,y1=0,x2=0,y2=0;
while(scanf("%d%d",&m,&n)!=EOF)
{
int **ptr=new int*[n+5];//
int **ps=new int*[n+5];//
for(int i=0;i<=n+1;i++)//
{
ptr[i]=new int[m+5];//
ps[i]=new int[m+5];//
}
for(int i=0;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
ps[i][j]=0;
ptr[i][j]=0;
}
}
scanf("%d",&x);
while(x--)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
ps[y1][x1]+=1;
ps[y2+1][x2+1]+=1;
ps[y2+1][x1]-=1;
ps[y1][x2+1]-=1;
}
int flag=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
ps[i][j]=ps[i][j]+ps[i-1][j]+ps[i][j-1]-ps[i-1][j-1];//关键
//cout<<ps[i][j]<<" ";
}
// cout<<endl;
}
for(int i=0;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(ps[i][j]>=1) ps[i][j]=1;
else ps[i][j]=0;
//cout<<ps[i][j]<<" ";
}
//cout<<endl;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
ps[i][j]=ps[i][j]+ps[i-1][j]+ps[i][j-1]-ps[i-1][j-1];
//cout<<ps[i][j]<<" ";
}
//cout<<endl;
}
scanf("%d",&y);
while(y--)
{
flag=0;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
if(ps[y2][x2]+ps[y1-1][x1-1]-ps[y1-1][x2]-ps[y2][x1-1]!=(x2-x1+1)*(y2-y1+1))//关键
flag=1;
if(flag==0)
cout<<"YES"<<endl;
else if(flag==1)
cout<<"NO"<<endl;
}
for(int i=0;i<=n+1;i++)
{
delete[]ptr[i];
delete[]ps[i];
}
delete[]ptr;
delete[]ps;
}
}
G - Molly’s Chemicals
题解:(前缀和)
就是前缀和来判断某个连续区间是否是p的非负幂级数
开始用的是p[i+1]-p[i]=k^m,暴力的话复杂度太高
然后化简一下p[i+1]=k^m+p[i],把p[i+1]的值标记,判断p[i+1]是否等于k ^ m+p[i]
这注意:一开始我是从开始就把p[i+1]的值全部标记了,就出现问题
1.由于数据的无序性,可能造成不满足一段连续区间的情况。
2.如果p[i+1]相同的值有很多,计算结果又不对。
然而展开为p[i+1]-k^m=p[i],并且对于不同的k ^m,就把p[i+1]的值重新标记一边。就不会出现上述问题,我果然还是太菜了…详细见代码
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];
ll vis[N];
ll p[N];
int n,m;
map<ll,int>d;
ll slove(ll x)
{
ll sum=0;
map<ll,int>c;
c[0]=1;
for(int i=1;i<=n;i++)
{
sum+=c[p[i]-x];
c[p[i]]++;
//cout<<c[p[i]-x]<<endl;
}
return sum;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//int x,y,x1,y1,x2,y2;
ll sum=0,mx=0;
cin>>n>>m;
ll ans=1;
for(int i=1;i<=n;i++)
{
cin>>a[i];
p[i]=p[i-1]+a[i];
}
if(m==1)
{
ll an=slove(1);
if(an>0) sum+=an;
}
else if(m==-1)
{
ll an=slove(1);
if(an>0) sum+=an;
an=slove(-1);
if(an>0) sum+=an;
}
else
for(int i=0;abs(ans)<1e15;i++)
{
ll an=slove(ans);
if(an>0) sum+=an;
ans*=m;
}
cout<<sum<<endl;
}
E - DNA Alignment
题解:(快速幂)
题目不难就是要找到规律:
最多且相同数目字母的个数^字符串长度
剩下就是快速幂就好了。
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];
int n;
char s[N];
ll mul(ll a,ll b,ll mod){//快速乘
ll ans=0,res=a;
while(b){
if(b&1) ans=(ans+res)%mod;
res=(res+res)%mod;
b>>=1;
}
return ans;
}
ll quickPower(ll a,ll b,ll mod){
ll ans=1,base=a;
while(b>0){
if(b&1)
ans=mul(ans,base,mod);
base=mul(base,base,mod);
b>>=1;
}
return ans;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int x,y,x1=0,y1=0,x2=0,y2=0;
ll sum=0,mx=0;
ll mod=1e9+7;
cin>>n;
scanf("%s",s);
for(int i=0;i<n;i++)
{
if(s[i]=='A')
x1++;
if(s[i]=='T')
x2++;
if(s[i]=='G')
y1++;
if(s[i]=='C')
y2++;
}
mx=max(x1,max(x2,max(y1,y2)));
if(mx==x1) sum++;
if(mx==y1) sum++;
if(mx==x2) sum++;
if(mx==y2) sum++;
cout<<quickPower(sum,n,mod)<<endl;
}
D - Vasya and Robot
题解:(二分+前缀和)
二分答案,枚举 (用前缀和表示的区间)判断是否符合条件,并找到满足条件的最小值。
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];
char str[N];
int xtr[N];
int ytr[N];
int n,dis;
int x,y;
int slove(int sta,int len)
{
int x1=xtr[n]-xtr[sta+len-1]+xtr[sta-1];
int y1=ytr[n]-ytr[sta+len-1]+ytr[sta-1];
int redis=abs(x-x1)+abs(y-y1);//计算除了区间以外所需要的步数
//cout<<sta<<" "<<len<<endl;
//cout<<x1<<" "<<y1<<endl;
//cout<<redis<<" "<<len<<endl;
//cout<<endl;
if(redis<=len&&redis%2==len%2)//临界条件:当可以操作的步数大于距离时满足条件
return 1;
return 0;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n;
scanf("%s",str+1);
for(int i=1;i<=n;i++)//分别计算x,y的前缀和
{
xtr[i]=xtr[i-1];
ytr[i]=ytr[i-1];
if(str[i]=='U') xtr[i]=xtr[i-1]+1;
if(str[i]=='D') xtr[i]=xtr[i-1]-1;
if(str[i]=='L') ytr[i]=ytr[i-1]-1;
if(str[i]=='R') ytr[i]=ytr[i-1]+1;
}
//cout<<xtr[n]<<" "<<ytr[n]<<endl;
cin>>y>>x;
dis=abs(x)+abs(y);
if(dis>n||(n-dis)%2!=0)// 特判
{
cout<<-1<<endl;
return 0;
}
int l=0,r=n,mid;
int ans=0;
while(l<=r)
{
mid=(l+r)>>1;
int flag=0;
for(int i=1;i<=(n-mid+1);i++)//区间起点的覆盖范围
{
if(slove(i,mid))
{
flag=1;
break;
}
}
if(flag==1)
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
cout<<ans<<endl;
}
Monitor
题解:(二分+前缀和)
提醒注意下变量的范围…太粗心了吧
以及初始话的问题(也是范围的因素…)
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];
char str[N];
int mapp[1000][1000];
int maps[1000][1000];
int n,m,k,q;
int x,y,z;
int slove(int x)
{
memset(maps,0,sizeof(maps));
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
if(mapp[i][j]<=x&&mapp[i][j]!=-1)
maps[i][j]=1;
maps[i][j]=maps[i][j]+maps[i-1][j]+maps[i][j-1]-maps[i-1][j-1];
if(i>=k&&j>=k&&maps[i][j]-maps[i-k][j]-maps[i][j-k]+maps[i-k][j-k]==k*k)
return 1;
}
return 0;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m>>k>>q;
int mx=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
mapp[i][j]=-1;
}
for(int i=0;i<q;i++)
{
cin>>x>>y>>z;
mapp[x][y]=z;
if(z>mx)
mx=z;
}
int l=0,r=mx,mid;
int ans=mod;
while(l<=r)
{
mid=(l+r)>>1;
if(slove(mid))
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
if(ans==mod)
cout<<-1<<endl;
else
cout<<ans<<endl;
}