正题
我好菜啊第二题都可以调40分钟。
A
这题很简单,暴力即可。
#include<bits/stdc++.h>
using namespace std;
int n;
long long a,b,r;
int main(){
scanf("%d %lld %lld %lld",&n,&a,&b,&r);r*=r;
long long x,y;
int tot=0;
for(int i=1;i<=n;i++){
scanf("%lld %lld",&x,&y);
if((x-a)*(x-a)+(y-b)*(y-b)<=r) tot++;
}
printf("%d\n",tot);
}
B
这题好烦啊,脑子非常乱,主要的做法就是找到一个三号点看一下最前面的两个点位置在哪里,最大化[2,3]两点的距离,或者找到一个四号点,看一下最前面的三个点在哪里,或者最大化[1,2]的距离,具体自己看看吧,写麻烦了。
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int n,T;
char s[N];
int last[N][4],pre[N][4],ans;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
scanf("%s",s+1);ans=-1;
last[0][0]=last[0][1]=last[0][2]=last[0][3]=0;
pre[n+1][0]=pre[n+1][1]=pre[n+1][2]=pre[n+1][3]=0;
for(int i=1;i<=n;i++){
last[i][0]=last[i-1][0];last[i][1]=last[i-1][1];
last[i][2]=last[i-1][2];last[i][3]=last[i-1][3];
if(s[i]=='p' && !last[i][0]) last[i][0]=i;
else if(s[i]=='i' && !last[i][1] && last[i][0]) last[i][1]=i;
else if(s[i]=='n' && !last[i][2] && last[i][1]) last[i][2]=i;
else if(s[i]=='k' && !last[i][3] && last[i][2]) last[i][3]=i;
}
for(int i=n;i>=1;i--){
pre[i][0]=pre[i+1][0];pre[i][1]=pre[i+1][1];
pre[i][2]=pre[i+1][2];pre[i][3]=pre[i+1][3];
if(s[i]=='p' && !pre[i][0] && pre[i][1]) pre[i][0]=i;
else if(s[i]=='i' && !pre[i][1] && pre[i][2]) pre[i][1]=i;
else if(s[i]=='n' && !pre[i][2] && pre[i][3]) pre[i][2]=i;
else if(s[i]=='k' && !pre[i][3]) pre[i][3]=i;
}
for(int i=1;i<=n;i++)
if(s[i]=='i' && last[i][0] && pre[i][2]) ans=max(ans,max(i-last[i][0]-1,pre[i][2]-i-1));
else if(s[i]=='n' && last[i][1] && pre[i][3]) ans=max(ans,max(i-last[i][1]-1,pre[i][3]-i-1));
printf("%d\n",ans);
}
}
C
要是考场上没想出来就亏大了,首先我们考虑加入一个区间会使得在l这个位置上有一个开头,在r+1位置上会多出来一个开头,并且把[l,r+1]这段的所有开头都去掉,所以我们把一个区间画成一条[l,r+1]的线段和l,r+1两个黑点,那么最后的权值就是从上往下看,黑点的个数,除了第n+1位,注意,一开始就有询问[1,n]。那么一个黑点产生贡献当且仅当不选那些比它晚并且覆盖它的询问。容易发现暴力即可。
#include<bits/stdc++.h>
using namespace std;
int n,m;
long long ans=1;
struct interval{
int x,y;
long long t1,t2;
}p[2010];
const long long mod=20050321;
int main(){
scanf("%d %d",&n,&m);
p[0].t1=1;p[0].x=1;
long long tot=1;
for(int i=1;i<=m;i++){
scanf("%d %d",&p[i].x,&p[i].y);p[i].y++;
(ans+=tot)%=mod;p[i].t1=tot;
if(p[i].y<=n) (ans+=tot)%=mod,p[i].t2=tot;
for(int j=0;j<i;j++){
if(p[j].x<p[i].x || p[i].y<p[j].x) (ans+=p[j].t1)%=mod,(p[j].t1*=2)%=mod;
if(p[j].y<p[i].x || p[i].y<p[j].y) (ans+=p[j].t2)%=mod,(p[j].t2*=2)%=mod;
}
(tot*=2)%=mod;
printf("%lld\n",ans);
}
}
D
首先考虑不断加入一个区间,并且把它覆盖,区间的交的和是O(n)的,考虑一次加入最多新产生两个区间就可以了。
考虑把询问按右端点时间从小到大排序。
我们对于一个每一个区间的交,记录一下上次覆盖这个位置是什么时候,单点加就好了,在那个时间减去原本的贡献,在这个时间加上新的贡献就好了,容易证明这个是正确的。
可惜的就是第二题花太多时间了导致这一题晚了8分钟AC。
#include<bits/stdc++.h>
#define ls now<<1
#define rs now<<1|1
using namespace std;
const int N=500010;
struct interval{
int x,y,c,id;
bool operator<(const interval q)const{
return y<q.y;
}
}p[N],X,Y;
set<interval> S;
set<interval>::iterator it;
long long sum[N*3];
struct ques{
int x,y,id;
bool operator<(const ques q)const{
return y<q.y;
}
}q[N];
long long ans[N];
int n,m,Q;
void add(int now,int x,long long t,int l,int r){
sum[now]+=t;
if(l==r) return ;
int mid=(l+r)/2;
if(x<=mid) add(ls,x,t,l,mid);
else add(rs,x,t,mid+1,r);
}
long long get_sum(int now,int x,int y,int l,int r){
if(x==l && y==r) return sum[now];
int mid=(l+r)/2;
if(y<=mid) return get_sum(ls,x,y,l,mid);
else if(mid<x) return get_sum(rs,x,y,mid+1,r);
else return get_sum(ls,x,mid,l,mid)+get_sum(rs,mid+1,y,mid+1,r);
}
int main(){
scanf("%d %d %d",&n,&m,&Q);
for(int i=1;i<=n;i++) scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].c);
for(int i=1;i<=Q;i++) scanf("%d %d",&q[i].x,&q[i].y),q[i].id=i;
sort(q+1,q+1+Q);
int now=0;
S.insert((interval){1,m,0,0});
for(int i=1;i<=Q;i++){
while(now<q[i].y){
now++;
it=S.lower_bound((interval){0,p[now].x,0,0});
while(it!=S.end()){
X=*it;
if(p[now].y<X.x) break;
if(X.x<=p[now].x && p[now].y<=X.y){
S.erase(it);
if(X.y>p[now].y) S.insert((interval){p[now].y+1,X.y,X.c,X.id});
if(X.x<p[now].x) S.insert((interval){X.x,p[now].x-1,X.c,X.id});
}
else if(p[now].x<=X.x && X.y<=p[now].y) S.erase(it);
else{
S.erase(it);
if(X.x<p[now].x) S.insert((interval){X.x,p[now].x-1,X.c,X.id});
else S.insert((interval){p[now].y+1,X.y,X.c,X.id});
}
if(X.id) add(1,X.id,-1ll*X.c*(min(p[now].y,X.y)-max(p[now].x,X.x)+1),1,n);
add(1,now,1ll*p[now].c*(min(p[now].y,X.y)-max(p[now].x,X.x)+1),1,n);
it=S.lower_bound((interval){0,p[now].x,0,0});
}
S.insert((interval){p[now].x,p[now].y,p[now].c,now});
}
ans[q[i].id]=get_sum(1,q[i].x,q[i].y,1,n);
}
for(int i=1;i<=Q;i++) printf("%lld\n",ans[i]);
}
E
这题就很简单了,直接缩点DAG上更新答案就可以了。
只是别忘了Tarjan要用tf标记。
#include<bits/stdc++.h>
using namespace std;
const int N=200010,M=500010;
struct edge{
int y,next,c;
}s[M];
int first[N],n,m,mmin[N],mmax[N],d1[N],d2[N],T,dfn[N],low[N],op=0,where[N],ans[N],in[N];
int sta[N],top=0,q,len;
bool tf[N];
queue<int> Q;
vector<edge> E[N];
void ins(int x,int y,int c){s[++len]=(edge){y,first[x],c};first[x]=len;}
void inss(int x,int y,int c){E[x].push_back((edge){y,0,c});in[y]++;}
void Tarjan(int x){
low[x]=dfn[x]=++op;
sta[++top]=x;tf[x]=true;
for(int i=first[x];i!=0;i=s[i].next) {
int y=s[i].y;
if(!dfn[y]) Tarjan(y),low[x]=min(low[x],low[y]);
else if(tf[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
d1[++T]=mmin[T]=1e9,d2[T]=mmax[T]=0;ans[T]=-1e9;
while(1){
int now=sta[top];top--;
where[now]=T;tf[now]=false;
if(now==x) break;
}
}
}
void Top_sort(){
Q.push(where[1]);
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int i=0;i<E[x].size();i++){
int y=E[x][i].y,c=E[x][i].c;
ans[y]=max(ans[y],max(max(d2[y],c)-mmin[x],mmax[x]-min(d1[y],c)));
ans[y]=max(ans[y],max(d2[y]-c,c-d1[y]));
ans[y]=max(ans[y],max(ans[x],0));
mmax[y]=max(mmax[y],max(c,mmax[x]));
mmin[y]=min(mmin[y],min(c,mmin[x]));
in[y]--;
if(!in[y]) Q.push(y);
}
}
}
int main(){
scanf("%d %d %d",&n,&m,&q);
int x,y,c;ans[0]=-1e9;
for(int i=1;i<=m;i++){scanf("%d %d %d",&x,&y,&c);ins(x,y,c);};
Tarjan(1);
for(x=1;x<=n;x++) if(dfn[x])
for(int i=first[x];i!=0;i=s[i].next)
if(where[s[i].y]!=where[x]) inss(where[x],where[s[i].y],s[i].c);
else d1[where[x]]=mmin[where[x]]=min(mmin[where[x]],s[i].c),d2[where[x]]=mmax[where[x]]=max(mmax[where[x]],s[i].c),ans[where[x]]=max(ans[where[x]],mmax[where[x]]-mmin[where[x]]);
Top_sort();
while(q--){
scanf("%d",&x);
if(ans[where[x]]==-1e9) printf("-1\n");
else printf("%d\n",ans[where[x]]);
}
}
F
有两种思路,一种就是按题解所说的,用一棵可持久化线段树优化建边,在开始的时候加入一个原节点,在结束的时候加入一个无用的节点就可以了,然后剩下的就是Tarjan找割点。
我的想法就是把一条横线段看成[r,x]-[l+n,x],然后一个竖线段相当于向[x,l]-[x+n,r]的横线段连边,具体就是建两棵KDT就可以了,时间复杂度垃圾了一些
没代码。