CODEFORCE ROUND #625 DIV2
不得不说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\n",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题有生之年吧