不得不说cf题目都是毒瘤,一篇读下来我头都裂开了 (哭)
个人认为这次的大坑点是E题 不谈有生之年的F题
和平常一样A,B,C都是思维题不过这次C题有坑还是贴一下
C题:
https://codeforces.ml/contest/1321/problem/C
题目大意就是给你n个字母,这个字母能被删除的条件是相邻的字母存在ASCII比他小1,问最多能删几个字母。
这里的坑就是你当前枚举的最高字母要多遍历几遍,如果只遍历一遍那
bbbbbbbb a bbbbb这种样例就会输出a前面一个加后面所有b的个数这样明显不对
刚开始不知道string有erase函数搞得我头皮发麻
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<set>
#include<list>
#include<vector>
#include<string>
using namespace std;
const int N=107;
int n,ans;
string s;
int main()
{
scanf("%d",&n);
getchar();
cin>>s;
for(int i='z';i>='a';i--)
{
for(int k=100;k>=0;k--)//多遍历几次
{
for(int j=0;j<s.size();j++)
{
if(i==s[j])
{
if(s[j]-s[j-1]==1||s[j]-s[j+1]==1)
{
ans++;
s.erase(j,1);
j--;
}
}
}
}
}
printf("%d",ans);
return 0;
}
D题:
https://codeforces.ml/contest/1321/problem/D
题目大意就是n个点m条单向边,没有重边自环大概 给你一条初始路径(u,v),判断这条路径上的点是否符合最短路径,如果不符合就会重新导航问你最大和最小重新导航的次数比如(u,v)=1,2,3,4有这样一个图:
从1到4的最短路是1,5,4而路径会从1到2,不满足最短路就重新导航,2到4的最短路有2, 3, 4,2,6,4两条,若求最大值则系统应显示出264这样从2到3系统就会重新导航一次,如果系统本身显示的是234则从2到3满足路线就不会重新导航(求最小值)
想想最短路的定义,dis i=dis j+w则是i到j的最短路,这里判断固定路线z中相邻两点是不是的最短路只需判断dis[a1]?=dis[a2]+1
如果等于:求最小值则ansminn不用加,求最大值则枚举a1的子节点若还存在一条最短路(dis children==dis a2)则ansmaxn++否则ansmaxn不加
如果不想等则肯定要重新导航ansminn,ansmaxn都加
求所有点到终点的最短路要反向建图
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<set>
#include<list>
#include<vector>
#include<string>
#include<queue>
using namespace std;
const int N=2e5+7,inf=0x3f3f3f3f;
int n,m,ansmaxn,ansminn,x,y,k,a[N],dis[N];
bool use[N];
queue < int > q;
vector < int > f1[N],f[N];
void spfa(int x)
{
for(int i=1;i<=n;i++)
dis[i]=inf;
//memset(dis,inf,sizeof(dis));
dis[x]=0;
q.push(x);
while(!q.empty())
{
int temp=q.front();
q.pop();
use[temp]=0;
for(int i=0;i<f[temp].size();i++)
{
int xx=f[temp][i];
if(dis[xx]>dis[temp]+1)
{
dis[xx]=dis[temp]+1;
if(use[xx]==0)
{
use[xx]=1;
q.push(xx);
}
}
}
}
return ;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&x,&y);
f[y].push_back(x);//反向建图求dis
f1[x].push_back(y);//正向建图求子节点
}
scanf("%d",&k);
for(int i=1;i<=k;i++)
scanf("%d",&a[i]);
spfa(a[k]);
/*for(int i=1;i<=n;i++)
{
printf("%d ",dis[i]);
}*/
for(int i=1;i<k;i++)
{
if(dis[a[i]]==dis[a[i+1]]+1)
{
for(int j=0;j<f1[a[i]].size();j++)//枚举子节点
{
int temp=f1[a[i]][j];
if(dis[temp]==dis[a[i+1]]&&temp!=a[i+1])
{
ansmaxn++;
break;
}
}
}
else
{
ansmaxn++;
ansminn++;
}
}
printf("%d %d",ansminn,ansmaxn);
return 0;
}
E题:
这题坑我快5个小时
现在过了我只想默默说一句 f**k
题目大意是输入n,m,p有n个攻击装备,m个防御装备,p个怪物每个怪物被打败后都会有钱并且怪物每个只能打一次
攻击装备和防御装备各买一个(即使怪物得到的钱很少也要买),且装备都有两个值 attack ,cost
能打败怪物的条件是n attack>p def && m defense>p atk注意怪物是先输入def再是atk而装备相反。
求获得的利润最大值 打败怪物获得的钱-装备花费的钱
后来才知道这样的东西叫 二维偏序 听说624的F题也是这个?!!
就是先将一维x放进线段树维护,另一维y排序,然后根据枚举的y ask线段树得出答案
这种代码是用一种特殊的方式排序,后来直接o1查询 值得借鉴
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<set>
#include<list>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
const int N=2e5+7,M=1e6+5;
const long long inf=0xfffffffff;
int n,m,p;
long long ca[M+5],cb[M+5],acost,bcost,a[N],b[N],ans=-inf;
//ca,cb数组小心越界
struct node
{
long long at,w,def;//atk==x装备的def==x
bool operator <(const node &t)const{return def<t.def;}
}c[N];
struct node3
{
long long sum,maxn,add;
}t[M*4];
long long max(long long x,long long y)
{
if(x>y)
return x;
return y;
}
void built(int p,int l,int r)
{
t[p].add=0;
if(l==r)
{
t[p].maxn=-cb[l];
//t[p].sum=-b[l].w;
return ;
}
int mid=(l+r)/2;
built(p*2,l,mid);
built(p*2+1,mid+1,r);
t[p].maxn=max(t[p*2].maxn,t[p*2+1].maxn);
//t[p].sum=t[p*2].sum+t[p*2+1].sum;
}
void push_tag(int p,int l,int r)
{
if(t[p].add)
{
t[p*2].add+=t[p].add;
t[p*2+1].add+=t[p].add;
t[p*2].maxn+=t[p].add;
t[p*2+1].maxn+=t[p].add;
t[p].add=0;
}
return ;
}
void push_down(int p)
{
t[p].maxn=max(t[p*2].maxn,t[p*2+1].maxn);
}
void up(int p,int qx,int zx,int gl,int gr,int k)//区间加操作
{
if(qx>=gl&&zx<=gr)
{
t[p].add+=k;
t[p].maxn+=k;
return ;
}
int mid=(qx+zx)/2;
push_tag(p,qx,zx);
if(gl<=mid)
up(p*2,qx,mid,gl,gr,k);
if(gr>mid)
up(p*2+1,mid+1,zx,gl,gr,k);
push_down(p);
return ;
}
long long askmaxn(int p,int qx,int zx,int gl,int gr)//查询最大值
{
if(qx>=gl&&zx<=gr)
{
return t[p].maxn;
}
int mid=(qx+zx)/2;
long long ans=-inf;
push_tag(p,qx,zx);
if(gl<=mid)
ans=max(ans,askmaxn(p*2,qx,mid,gl,gr));
if(gr>mid)
ans=max(ans,askmaxn(p*2+1,mid+1,zx,gl,gr));
return ans;
}
int main()
{
scanf("%d %d %d",&n,&m,&p);
for(int i=1;i<=M+1;i++)
{
ca[i]=inf;//攻击为i时所要的最小的钱
cb[i]=inf;//防御为i时所要的最小的钱
}
for(int i=1;i<=n;i++)
{
scanf("%lld %lld",&a[i],&acost);
ca[a[i]]=min(ca[a[i]],acost);
}
for(int i=1;i<=m;i++)
{
scanf("%lld %lld",&b[i],&bcost);
cb[b[i]]=min(cb[b[i]],bcost);
}
for(int i=M;i>=1;i--)
{
ca[i]=min(ca[i],ca[i+1]);
cb[i]=min(cb[i],cb[i+1]);//如果防御力为i存在铠甲则有i-1防御力铠甲的代价最小仍为cb[i]
}
built(1,1,M);//防御力线段树
for(int i=1;i<=p;i++)
scanf("%lld %lld %lld",&c[i].def,&c[i].at,&c[i].w);
sort(c+1,c+p+1);//monster 防御力从小到大
ans=-ca[1]-cb[1];
//printf("%lld %lld
",ca[1],cb[1]);
for(int i=1;i<=p;i++)
{
up(1,1,M,c[i].at+1,M,c[i].w);
//先假设剑的攻击力都足以破防,则防御力大于等于c[i].at+1的装备全部可以得到c[i].w的钱
ans=max(ans,askmaxn(1,1,M,1,M)-ca[c[i].def+1]);
//防御力已经满足则只需要减去攻击力大于等于c[i].def+1的购买费用即可
//因为防御力已经排序了,所有后来的monster防御力一定大于先前的
//当前选择的剑攻击力一定能破之前怪的防此时满足up的假设 故up函数成立
}
printf("%lld",ans);
return 0;
}
这种是我最开始写的,因为inf 是int 类型(和上面代码开始犯的错一样)没过我还以为思路或者线段树写错了...结果没错
就是这个const long long inf=0xfffffffff坑死个人
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<set>
#include<list>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
const int N=2e5+7;
const long long inf=0xfffffffff;
int n,m,p,pos1,pos2,pos3;
long long mat[N],mde[N],mv[N],de[N],ans=-inf;
bool use[N];
struct node
{
long long x,w,def;//atk==x?????def==x
bool operator <(const node &t)const{return x<t.x;}
}a[N],b[N],c[N];
struct node3
{
long long sum,maxn,add;
}t[N*4];
long long max(long long x,long long y)
{
if(x>y)
return x;
return y;
}
void built(int p,int l,int r)
{
t[p].add=0;
if(l==r)
{
t[p].maxn=-a[l].w;
//t[p].sum=-b[l].w;
return ;
}
int mid=(l+r)/2;
built(p*2,l,mid);
built(p*2+1,mid+1,r);
t[p].maxn=max(t[p*2].maxn,t[p*2+1].maxn);
//t[p].sum=t[p*2].sum+t[p*2+1].sum;
}
void push_tag(int p,int l,int r)
{
if(t[p].add)
{
t[p*2].add+=t[p].add;
t[p*2+1].add+=t[p].add;
t[p*2].maxn+=t[p].add;
t[p*2+1].maxn+=t[p].add;
t[p].add=0;
}
return ;
}
void push_down(int p)
{
t[p].maxn=max(t[p*2].maxn,t[p*2+1].maxn);
}
void up(int p,int qx,int zx,int gl,int gr,int k)
{
if(qx>=gl&&zx<=gr)
{
t[p].add+=k;
t[p].maxn+=k;
return ;
}
int mid=(qx+zx)/2;
push_tag(p,qx,zx);
if(gl<=mid)
up(p*2,qx,mid,gl,gr,k);
if(gr>mid)
up(p*2+1,mid+1,zx,gl,gr,k);
push_down(p);
return ;
}
long long askmaxn(int p,int qx,int zx,int gl,int gr)
{
if(qx>=gl&&zx<=gr)
{
return t[p].maxn;
}
int mid=(qx+zx)/2;
long long ans=-inf;
if(gl<=mid)
ans=max(ans,askmaxn(p*2,qx,mid,gl,gr));
if(gr>mid)
ans=max(ans,askmaxn(p*2+1,mid+1,zx,gl,gr));
return ans;
}
int main()
{
scanf("%d %d %d",&n,&m,&p);
for(int i=1;i<=n;i++)
scanf("%lld %lld",&a[i].x,&a[i].w);
for(int i=1;i<=m;i++)
scanf("%lld %lld",&b[i].x,&b[i].w);
for(int i=1;i<=p;i++)
scanf("%lld %lld %lld",&c[i].def,&c[i].x,&c[i].w);
sort(a+1,a+n+1);
sort(b+1,b+m+1);
sort(c+1,c+p+1);
for(int i=1;i<=p;i++)
{
mat[i]=c[i].x;
mv[i]=c[i].w;
mde[i]=c[i].def;
}
for(int i=1;i<=n;i++)
de[i]=a[i].x;
built(1,1,n);
for(int i=1;i<=m;i++)
{
if(b[i].x>mat[p])
pos2=p;
else
pos2=lower_bound(mat+1,mat+p+1,b[i].x)-mat-1;
for(int j=pos1+1;j<=pos2;j++)
{
//printf("#@$%d %d ",de[n],mde[j]);
if(de[n]<=mde[j])
continue;
else
pos3=upper_bound(de+1,de+n+1,mde[j])-de;
//printf("pos3=%d ", pos3);
up(1,1,n,pos3,n,mv[j]);
pos1=pos2;
}
ans=max(ans,askmaxn(1,1,n,1,n)-b[i].w);
//printf("%lld ",askmaxn(1,1,n,1,n));
}
printf("%lld",ans);
return 0;
}
E题已经让我憔悴不堪,F题有生之年吧