前面的碎碎念:
本次比赛题目比较对胃口,一个半小时就A了前五题,尤其是第二题,我正好做过它的加强版(笑)。现在比赛rating还在计算,希望能借此上个绿名吧。
题解部分:
A-打怪
签到题。当我们的攻击力大于等于怪物的总血量,每次我们都可以不耗血秒杀怪物,从而可以杀死无数只怪物,对此输出-1。当我们的攻击力小于怪物的总血量时,我们设打死一只怪物需要round个回合,而每打死一只怪物,我们将会减少(round-1)A的血量(因为最后一回合我们杀死了怪物,因而少受一回合怪物的攻击)。设答案为ans,那么我们就是要寻找满足ans
(round-1)
A<h最大的那个ans,对此我们二分查找这个ans即可。
时间复杂度:O(tlog(h))。
代码部分:
#include<bits/stdc++.h>
using namespace std;
int main()
{
long long T,h,a,H,A,ans,round,l,r,m;
scanf("%lld",&T);
while(T--)
{
scanf("%lld%lld%lld%lld",&h,&a,&H,&A);
if(a>=H)printf("-1\n");
else
{
round=H/a+(H%a?1:0);
for(l=0,r=1e9;l<=r;)
{
m=(l+r)>>1;
if(m*(round-1)*A<h)ans=m,l=m+1;
else r=m-1;
}
printf("%lld\n",ans);
}
}
return 0;
}B-吃水果
运气好做过这个题的加强版本2333,需要找规律。首先我们令ans等于n与m中较大的那个数,对于较小的那个数,使其不断乘2,直到其大于等于较大的那个数。令ans加上这个数乘二的次数,就是最终的答案。为什么呢?比如对于数字3 4,我们先减两次1,变成1 2,再使1乘2变成2,再减两次1使其同时为0。所以说,我们在减的过程中,总有机会会使较小的那个数乘2正好等于较大的那个数,最后就能同时使其为0。
时间复杂度:O(Tlog(n))。
代码部分:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int i,T,n,m,ans;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
if(n>m)swap(n,m);
ans=m;
for(i=n;i<m;i<<=1)ans++;
printf("%d\n",ans);
}
return 0;
}C-四个选项
看到12个题目,每个题目才4个选项,很容易想到爆搜4^12种结果,加上每个选项有固定数量的限制,实际要搜索的结果数会更少。至于m个额外条件,我们先利用并查集预处理出每个题目的根节点题目,在最后统计时验证每个题目是否与其根节点题目的答案相同即可。
时间复杂度:O(124^12)。
代码部分:
#include<bits/stdc++.h>
using namespace std;
int na,nb,nc,nd,m,ans=0,V[15],T[15];
int find(int a)
{
if(a==V[a])return a;
return V[a]=find(V[a]);
}
void DFS(int L,int a,int b,int c,int d)
{
int i;
if(L==12)
{
if(a!=na||b!=nb||c!=nc||d!=nd)return;
for(i=1;i<=12;i++)if(T[i]!=T[V[i]])break;
if(i<=12)return;
ans++;
return;
}
T[L+1]=1,DFS(L+1,a+1,b,c,d);
T[L+1]=2,DFS(L+1,a,b+1,c,d);
T[L+1]=3,DFS(L+1,a,b,c+1,d);
T[L+1]=4,DFS(L+1,a,b,c,d+1);
}
int main()
{
int i,j,k,x,y;
scanf("%d%d%d%d%d",&na,&nb,&nc,&nd,&m);
for(i=1;i<=12;i++)V[i]=i;
while(m--)
{
scanf("%d%d",&x,&y);
j=find(x),k=find(y);
if(j!=k)V[j]=k;
}
DFS(0,0,0,0,0);
printf("%d\n",ans);
return 0;
}D-最短路变短了
我们先利用堆优化的dij求出从1号节点到各点的最短距离,把所有边反向再跑一次dij,计算出从n号节点到各点的最短距离,这时我们可以得到原始情况下1号节点到n号节点的最短距离。那么对于任意一条边(u,v,w),若把边的方向反向能够使最短路变短,那么这条变短后的最短路肯定是经过了这条边的,所以这条最短路的长度肯定是1号节点到v号节点的距离+w+u号节点到n号节点的距离。我们把这个距离和原始的1号节点到n号节点的最短距离比较,若其小于原始的最短距离输出YES,否则输出NO。
时间复杂度:O(nlog(n)+q)。
代码部分:
#include<bits/stdc++.h>
using namespace std;
struct node
{
long long x,h;
bool operator<(const node &a)const
{
return a.h<h;
}
}r,f;
struct node2
{
int x,y,w;
}E[200005];
priority_queue<node>Q;
int n,m,q;
long long ans,D[2][100005];
vector<int>R[2][100005],W[2][100005];
void dij(bool a,int sx)
{
int i,j;
for(i=1;i<=n;i++)D[a][i]=2e18;
r.h=D[a][sx]=0,r.x=sx,Q.push(r);
while(!Q.empty())
{
f=Q.top(),Q.pop();
if(D[a][f.x]<f.h)continue;
for(i=0;i<R[a][f.x].size();i++)
{
j=R[a][f.x][i];
if(D[a][j]<=f.h+W[a][f.x][i])continue;
r.h=D[a][j]=f.h+W[a][f.x][i],r.x=j,Q.push(r);
}
}
}
int main()
{
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].w);
R[0][E[i].x].push_back(E[i].y),R[1][E[i].y].push_back(E[i].x);
W[0][E[i].x].push_back(E[i].w),W[1][E[i].y].push_back(E[i].w);
}
dij(0,1),dij(1,n),ans=D[0][n];
scanf("%d",&q);
while(q--)
{
scanf("%d",&i);
if(D[0][E[i].y]+D[1][E[i].x]+E[i].w<ans)printf("YES\n");
else printf("NO\n");
}
return 0;
}E-相似的子串
题目本质上就是要我们求,在主串中选出k个不相交且相同的子串,这个子串的最长长度是多少。很显然,这个长度越长,我们就越难在主串中选出这k个不相交且相同的子串,其具有单调性可以二分。那么我们先利用字符串哈希预处理出Hash和Power数组,接下来二分答案ans,每次判断ans是否合法,也就是判断我们能否在主串中选出k个不相交且相同的子串,并且这个子串长度为ans。
至于判断部分,我们从前往后扫描主串,利用哈希表V记录长度为len的子串的位置,用哈希表T记录长度为len的子串的个数。每次判断当前长度为len的子串,其V记录的上一个位置是否合法(也就是会不会相交),若合法更新V和T即可。若T记录的某子串个数大于等于k了,返回1表示这个长度len合法。若扫描完整个字符串也未能返回1,我们就返回0表示这个长度len非法。
时间复杂度:O(nlog(n))。
代码部分:
#include<bits/stdc++.h>
using namespace std;
int n,k;
unordered_map<unsigned long long,int>V,T;
unsigned long long Hash[200005]={0},Power[200005],base=1e9+7;
char R[200005];
bool judge(int len)
{
int i;
unsigned long long t;
V.clear(),T.clear();
for(i=len;i<=n;i++)
{
t=Hash[i]-Hash[i-len]*Power[len];
if(!V[t])V[t]=i,T[t]++;
else if(V[t]<=i-len)V[t]=i,T[t]++;
if(T[t]>=k)return 1;
}
return 0;
}
int main()
{
int i,l,r,mid,ans;
scanf("%d%d%s",&n,&k,R+1);
for(Power[0]=i=1;i<=n;i++)
{
Hash[i]=Hash[i-1]*base+R[i]-'a'+1;
Power[i]=Power[i-1]*base;
}
for(l=0,r=n/k;l<=r;)
{
mid=(l+r)>>1;
if(judge(mid))ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}若有疏漏之处,还望大佬轻喷。

京公网安备 11010502036488号