1.神奇天平
思路:
可以称m份就分成m+1份,然后对多的那份继续分。
直至最后只剩一份。
考察:思维题
代码如下:
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n,m,ans=0;
cin>>n>>m;
m++;
while(n>1){
n=n/m+(n%m!=0);
ans++;
}
cout<<ans<<endl;
}
return 0;
}
2.魔法学院(easy version)
简单版本:数据范围较小,直接枚举改变即可,
题意:
给出一个字符串,有m种魔法,每种魔法都可以使用无数次。
第i种魔法可以将对应区间中的一个字符替换为给出的字符。
进行若干次操作之后,问字符串的最大价值和是多少。
思路:
因为每种魔法可以使用无限次,因此直接枚举这段区间的
每个字符,替换为更大的字符,最后求出ASCII总和。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int n,m,l,r,sum;
char c,s[N];
int main(){
scanf("%d%d%s",&n,&m,s);
while(m--){
cin>>l>>r>>c;
for(int i=l-1;i<=r-1;i++){
s[i]=max(s[i],c);
}
}
for(int i=0;i<n;i++){
sum+=s[i];
}
cout<<sum<<endl;
return 0;
}
3.魔法学院(hard version)
题意:和上一题一样,只不过数据范围大了,因此用上题做法会超时。
思路:
把所有修改用结构体存储起来,按字符从大到小进行排序,
一个点如果最多被最大的那个字符修改一次,
也就是说 只需要修改区间[l,r]中没被修改过的点即可。
可以用并查集加速查找没有被修改过的点的过程。
如果一个点被修改过,直接跳过,否则,
当这个点没被修改过时,即fa[i]=i,当i被修改过之后,
令fa[i]=i+1,即当前点的右边一个点很可能就是下一个需要
修改的点,以i+1号点为起点,寻找下一个需要被修改的
点的位置,也就是说在[l,r]不断找fa[i]=i的点。
考察:
并查集(使用并查集来记录后一个应该被访问的点是哪个/并查集加速查找)
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7+10;
int n,m,fa[N];
char s[N],c[N];
struct node{
int l,r;
char c;
bool operator<(const node &t)const{
return c>t.c;
}
}q[N];
int find(int x){//并查集得查找操作
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);//路径压缩
}
int main(){
scanf("%d %d",&n,&m);
scanf("%s",s+1);
for(int i=0;i<=n+1;i++) fa[i]=i;//初始化
for(int i=1;i<=m;i++){
scanf("%d %d %c",&q[i].l,&q[i].r,&q[i].c);
}
sort(q+1,q+m+1);//按字符从大到小排序
for(int i=1;i<=m;i++){
int l=q[i].l,r=q[i].r;
while(l<=r){
c[l]=max(c[l],q[i].c);//修改
fa[l]=l+1;//当前点右边相邻点可能就是下一个需要修改的点
l=find(fa[l]);//以i+1号为起点,寻找下一个fa[i]=i的点 加速查找
}
}
ll ans=0;
for(int i=1;i<=n;i++){
ans+=max(c[i],s[i]);
}
cout<<ans<<endl;
return 0;
}
4.监狱逃亡
思路:
首先预处理出前缀和数组,
用i表示第一行下到第二行下的坐标,用j表示第二行下到第三行下的坐标。
那么问题就转化为求满足
sum1[i]+sum2[j]-sum2[i-1]+sum3[n]-sum3[j-1]>=0(i<=j)
的(i,j)对数。
即sum1[i]-sum2[i-1]>=sum3[j-1]-sum2[j]-sum3[n];
考察:
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
const int mod = 1e9+7;
#define lowbit(x) x&(-x)
#define pb push_back
int n,cnt,tr[N];
ll sum[3][N],ans;
vector<ll> v;
int ask(int x){
int s=0;
for(;x;x-=lowbit(x)) s+=tr[x];
return s;
}
void add(int x){
for(;x<=cnt;x+=lowbit(x)) tr[x]++;
}
int get(ll x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
//确定x在去重后升序排列的数组中的位置,这里注意要加1,因为树状数组从1开始存
}
int main(){
scanf("%d",&n);
for(int i=0;i<3;i++){
for(int j=1;j<=n;j++){
scanf("%lld",&sum[i][j]);
sum[i][j]+=sum[i][j-1];//前缀和
}
}
for(int i=1;i<=n;i++){
v.pb(sum[0][i]-sum[1][i-1]);
v.pb(sum[2][i-1]-sum[1][i]-sum[2][n]);
}
sort(v.begin(),v.end());//排序
v.erase(unique(v.begin(),v.end()),v.end());//去重
cnt=v.size();//求出去重后的元素个数
for(int i=n;i;i--){
ll x=get(sum[0][i]-sum[1][i-1]);
ll y=get(sum[2][i-1]-sum[1][i]-sum[2][n]);
add(y);
ans=(ans+ask(x)%mod)%mod;
ans=(ans+mod)%mod;
}
printf("%lld\n",ans);
return 0;
}