目录
G1. Teleporters (Easy Version)
G2. Teleporters (Hard Version)
A. Codeforces Checking
简单题简单处理即可
void solve()
{
string b; cin>>b;
if(a.find(b)!=-1) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
return ;
}
B. Following Directions
简单模拟
void solve()
{
string s; cin>>n>>s;
int x=0,y=0;
for(int i=0;i<n;i++)
{
if(s[i]=='U') y++;
if(s[i]=='R') x++;
if(s[i]=='L') x--;
if(s[i]=='D') y--;
if(x==1&&y==1)
{
cout<<"YES"<<endl;
return ;
}
}
cout<<"NO"<<endl;
return ;
}
C. Prepend and Append
简单两边同时移动即可
void solve()
{
string s; cin>>n>>s;
s=' '+s;
for(int i=1,j=n;i<=j;i++,j--)
{
if((s[i]-'0')+(s[j]-'0')!=1)
{
cout<<j-i+1<<endl;
return ;
}
}
cout<<0<<endl;
return ;
}
典型
D. Distinct Split
我们发现一直是从中间某一个位置划开也就是一段要么是前面的要么是后面的也就是说
我们可以发现如果后面的没了也就是前面的有了我们可以直接从前面来变化记录后面的
变化同时就可以记录前面的变化
也可以傻傻的使用树状数组(不推荐)局限性
void solve()
{
cin>>n;
mp1.clear(),mp2.clear();
string s; cin>>s;
int ans1=0,ans2=0;
for(int i=0;i<n;i++)
{
if(!mp2[s[i]]) ans2++;
mp2[s[i]]++;
}
int ans=ans2;
for(int i=0;i<n;i++)
{
if(!mp1[s[i]]) ans1++;
mp1[s[i]]++;
mp2[s[i]]--;
if(mp2[s[i]]==0) ans2--;
ans=max(ans,ans1+ans2);
}
cout<<ans<<endl;
E. Negatives and Positives
发现性质最后一定是都会处理到所以的话我们判断负数的个数,0的个数结合贪心最大的负数和最最小的正数即可
void solve()
{
cin>>n;
vector<int> z,f;
int cnt=0;
for(int i=1;i<=n;i++){
int x; cin>>x;
if(x==0) cnt++;
else{
if(x>0) z.push_back(x);
else f.push_back(x);
}
}
sort(z.begin(),z.end());
sort(f.begin(),f.end(),greater<int>());
LL ans=0;
for(auto&v:z) ans+=v;
for(auto&v:f) ans-=v;
if(cnt || f.size()%2==0){
}
else{
if(z.empty()) ans+=2*f[0];// 全是负数同时它是奇数
else ans=ans-z[0]+f[0]+max(-z[0]-f[0],z[0]+f[0]);
}
cout<<ans<<endl;
return ;
}
F. Range Update Point Query
我们发现这种操作次数不是很多的同时是维护区间性质的,维护区间的变化同时又要查询我们可以考虑使用树状数组
struct BIT{
int tr[N];
int inline lowbit(int x){
return x&(-x);
};
void add(int k,int x){
for(int i=k;i<=n;i+=lowbit(i)) tr[i]+=x;
};
int query(int k){
int res=0;
for(int i=k;i;i-=lowbit(i)) res+=tr[i];
return res;
};
}tree;
void solve()
{
cin>>n>>m;
vector<int> a(n+5);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n+5;i++) tree.tr[i]=0;
while(m--){
int op,l,r,x;
cin>>op;
if(op==1){
cin>>l>>r;
tree.add(l,1),tree.add(r+1,-1);
}
else{
cin>>x;
int tim=tree.query(x);
int w=a[x];
while(tim>0 && w>=10){
int now=0;
while(w){
now+=w%10;
w/=10;
}
w=now;
tim--;
}
cout<<w<<endl;
}
}
return ;
}
ps:同时我们可以发现操作次数不算多用了之后有些区间就消失了所以可以使用二分+set
int change(int x)
{
int res=0;
while(x) res+=x%10,x/=10;
return res;
}
void solve()
{
cin>>n>>m;
q.clear();
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]>=10) q.insert(i);
}
while(m--)
{
int x; cin>>x;
if(x==1){
int l,r; cin>>l>>r;
auto rr=q.upper_bound(r);
auto ll=q.lower_bound(l);
for(auto i=ll;i!=rr;)// 遍历次数不会太多比如999999 也就54 那么也就遍历3n就没了
{
auto it=*i;
if(a[it]>=10) a[it]=change(a[it]);
if(a[it]<10) i=q.erase(i);
else i++;
}
}
else {
int y; cin>>y;
cout<<a[y]<<endl;
}
}
return ;
}
G1. Teleporters (Easy Version)
我们发现一个传送器的贡献是a[i]+i,所以我们直接从小到大最优的选取即可
int a[N];
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]+=i;
}
sort(a+1,a+1+n);
int ans=0;
for(int i=1;i<=n;i++)
if(m>=a[i]) m-=a[i],ans++;
else break;
cout<<ans<<endl;
return ;
}
G2. Teleporters (Hard Version)
我们考虑这样一个问题也就是如果上一个点走完了我去完0或者是n+1都是可以的取决于谁带来的贡献小,于是有这样一个贪心,每一个点一定是从左边或者右边来的,接着第一个点一定是从左边来的所有我们枚举第一个点的位置减去他的共享接着来算选了多少个点,如果重复算了这个点的话我们就重新计算他带来的贡献即可
LL a[N],b[N],c[N],sum[N];
LL find(LL x)
{
LL l=0,r=n;
while(l<r){
LL mid=l+r+1>>1;
if(sum[mid]>x) r=mid-1;
else l=mid;
}
return l;
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];// 初始序列
b[i]=min(a[i]+i,a[i]+n-i+1);// 最优序列
c[i]=b[i];// 初始最优序列
}
sort(b+1,b+1+n);// 对最优序列排序
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+b[i];// 前缀和
LL ans=0;
for(int i=1;i<=n;i++){
LL res=m;
if(a[i]+i>res) continue;
res-=(a[i]+i);
LL l=find(res);
if(b[l]<c[i]) ans=max(ans,l+1);// 表示比排序后的还要大表示不会多算自己
else ans=max(ans,find(res+c[i]));// 表示这个点重复算了加上他重复的贡献即可
}
cout<<ans<<endl;
return ;
}