题目链接:http://codeforces.com/contest/813
Problem A
计算做完所有题的时间总和sum,如果sum出现在某段区间内,那么最早时间就是sum,否则就是最靠近sum的之后开启提交的时间。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<set>
#define ll long long
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define debug(x) printf("----Line%s----\n",#x)
using namespace std;
const int N = 1e5+5;
int main()
{
//freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
//freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
//int T;scanf("%d",&T);while (T--){}
//int n,m;while (~scanf("%d %d",&n,&m)){}
int n,x,cnt=0,maxtime=-1;
scanf("%d",&n);
for1(i,1,n) scanf("%d",&x),cnt+=x;
int m,l,r,zao=100000000;
scanf("%d",&m);
for1(i,1,m){
scanf("%d %d",&l,&r);
if (l<=cnt && cnt<=r) zao = cnt;
if (l>=cnt) zao = min(zao,l);
maxtime = max(maxtime,r);
}
if (cnt<=maxtime){
printf("%d\n",zao);
}
else printf("-1\n");
return 0;
}
Problem B
我们发现算很快就会超过1e18,所以我们枚举出x,y所有小于1e18的可能情况,每个数最多五六十个,注意别把
漏掉了
然后我们统计出两个数的和所有可能出现的情况记做cant[],两层五六十的for循环搞定,我们对所有值排序去重,然后开头添一个0,结尾添一个1e18+1
于是我们的区间就被分成了
0 - cant[1] - cant[2] -cant[3] - .........-1e18+1
这都是不能取的边界,然后每次枚举一段区间,分析[l,r]六种情况(根据当前枚举左右边界跟l,r的关系)就可以了。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<set>
#define ll long long
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define debug(x) printf("----Line%s----\n",#x)
const ll INF = (ll)1e18;
using namespace std;
const int N = 1e5+5;
ll cant[50000];
ll xx[5000],yy[5000];
int main()
{
//freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
//freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
//int T;scanf("%d",&T);while (T--){}
//int n,m;while (~scanf("%d %d",&n,&m)){}
ll x,y,l,r;
while (~scanf("%I64d %I64d %I64d %I64d",&x,&y,&l,&r)){
//if (y==INF) printf("YES\n");
int totx=0,toty=0,tot=1;
///枚举所有x的单独情况
ll now = x;
xx[totx++] = x;//debug(333);
xx[totx++] = 1;//debug(444);
while (now<=INF/x){//debug(555);
now = now*x;
xx[totx++] = now;
}//debug(666);
///枚举y
now = y;
yy[toty++] = y;
yy[toty++] = 1;
while (now<=INF/y){
now = now*y;
yy[toty++] = now;
}//debug(777);
///枚举所有不可以的值
//for0(i,0,totx) cant[tot++] = xx[i];
//for0(i,0,toty) cant[tot++] = yy[i];
for0(i,0,totx){
for0(j,0,toty){
if (xx[i] <= INF-yy[j]){
cant[tot++] = xx[i]+yy[j];
}
}
}//debug(888);
sort(cant+1,cant+tot);///只是从1开始,右界不要习惯性+1
int cnt = unique(cant+1,cant+tot)-cant;
//for0(i,0,cnt) printf("cant[%d]=%I64d\n",i,cant[i]);
ll maxlen = 0;
cant[cnt++] = INF+1;
for0(i,1,cnt){ ///(cant[i-1],cant[i]) 找最大值
if (l>=cant[i]){
continue;
}
else if (r<=cant[i-1]){
continue;
}
else if (l>cant[i-1] && r<cant[i]){
maxlen = r-l+1;
break;
}
else if (l<=cant[i-1] && r>=cant[i]){
maxlen = max(maxlen,cant[i]-1-(cant[i-1]+1)+1);
}
else if (l<=cant[i-1] && r<cant[i]){
maxlen = max(maxlen,r-(cant[i-1]+1)+1);
}
else if (r>=cant[i] && l<cant[i]){
maxlen = max(maxlen,cant[i]-1-l+1);
}
}
printf("%I64d\n",maxlen);
}
return 0;
}
Problem C
小A的出发点对树进行一次遍历,记录每个点的深度,理解为A在第几个时刻就可以到达这个点。
然后从B出发遍历一次树,遍历的时候记录当前走了多少步,如果到达的这个点的深度小于等于B已经走的步数,说明走这个点就被抓住了,直接return,我们找寻整个过程到达的深度最大的点(也有可能就是出发点最深,那么就坐以待毙就行了)
最后 最深*2 就是两个人的总共步数了
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<set>
#define ll long long
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define debug(x) printf("----Line%s----\n",#x)
using namespace std;
const int N = 2e5+5;
struct node{
int to,last;
}edge[N<<1];
int head[N],id=1;
void add(int u,int v){edge[id].to = v;edge[id].last=head[u];head[u]=id++;}
int dep[N],n,x;///记录深度
void predfs(int now,int pre,int depth)///从1节点遍历,得出所有节点的深度
{
dep[now] = depth;
for (int i=head[now];i!=0;i=edge[i].last){
int v = edge[i].to;
if (v!=pre) predfs(v,now,depth+1);
}
}
int maxdepth=dep[x];
void dfs(int now,int pre,int cnt)///在满足当前累计步数<=当前节点深度时求出B能到达的最大深度:坐以待毙/找到能到达的最深的地方
{
if (cnt>=dep[now]) return ;
maxdepth = max(maxdepth,dep[now]);
for (int i=head[now];i!=0;i=edge[i].last){
int v = edge[i].to;
if (v!=pre) dfs(v,now,cnt+1);
}
}
int main()
{
//freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
//freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
//int T;scanf("%d",&T);while (T--){}
//int n,m;while (~scanf("%d %d",&n,&m)){}
scanf("%d %d",&n,&x);
for1(i,1,n-1){
int u,v;
scanf("%d %d",&u,&v);
add(u,v);
add(v,u);
}
predfs(1,1,-1);
dfs(x,x,-1);
printf("%d\n",(maxdepth+1)*2);
return 0;
}
Problem E
每个位置的数预处理出该类型往前k个的那个数的位置,如果不存在就就等于-1,这个数记做val[]
这个可以O(N)实现,然后每次我们只要统计询问区间内val[]小于l的数就行了,因为小于l证明当前位置,不管是哪种士兵,他反正没有取够k个。
用一下分块,每个块维护块内val递增序列,完整的块二分找位置,边界暴力就行。
复杂度大概是O( q*sqrt(N)*logN)
代码:
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#define ll long long
#define pb push_back
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
using namespace std;
const int N = 1e5+5;
const int M = 400;
int n,k,blo,bl[N];
vector<int>pos[N],v[M];
int val[N];
int query(int l,int r)
{
int ans = 0;
for (int i=l;i<=min(bl[l]*blo,r);i++)
if (val[i]<l) ans++;
if (bl[l]!=bl[r]){
for (int i=(bl[r]-1)*blo+1;i<=r;i++)
if (val[i]<l) ans++;
}
for (int i=bl[l]+1;i<=bl[r]-1;i++){
ans += lower_bound(v[i].begin(),v[i].end(),l)-v[i].begin();
}
return ans;
}
int main()
{
scanf("%d %d",&n,&k);
blo = sqrt(n);
int maxval = 0;
for1(i,1,n){
scanf("%d",val+i);
bl[i] = (i-1)/blo+1;
pos[val[i]].pb(i);
//printf("pos[%d].pb(%d)\n",val[i],pos[val[i]][0]);
maxval = max(maxval,val[i]);
}
for1(i,1,maxval){///千万不要只搜索到n!!!!
if (pos[i].empty()) continue;
for (int j=0;j<pos[i].size();j++){
if (j<k) val[pos[i][j]] = -1;//,printf("val[%d]=%d",pos[i][j],val[pos[i][j]]);
else val[pos[i][j]] = pos[i][j-k];
}
}
for1(i,1,n){
v[bl[i]].pb(val[i]);
//printf("val[%d]=%d\n",i,val[i]);
}
for1(i,1,bl[n]) sort(v[i].begin(),v[i].end());
int m,l,r;
int ans = 0;
scanf("%d",&m);
while (m--){
scanf("%d %d",&l,&r);
l = (l+ans)%n+1;
r = (r+ans)%n+1;
if (l>r) swap(l,r);
//printf("l=%d r=%d\n",l,r);
ans = query(l,r);
printf("%d\n",ans);
}
return 0;
}
总结:
1.a^x我们需要清楚他很容易就超过题目的询问范文。
2.C题不可能两个点一起动,先试着解决一个
3.E题预处理数据的方法真的很秀,通过得出前推k个的位置,把问题直接转化为了区间询问的问题,这种思路值的考虑。