比赛传送门
前几天并没有打这场比赛,但是听说这场题目蛮水的(我的一位初三dalao朋友直接AK了),所以昨天打了打Virtual participation
先上题解:
A:
直接暴力枚举匹配即可,就是我写得麻烦了一点
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
using namespace std;
inline int read(){
int k=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
return k*f;
}
char s[100010];int n;
const char c[5][7]={
' ','D','a','n','i','l',' ',
' ','O','l','y','a',' ',' ',
' ','S','l','a','v','a',' ',
' ','A','n','n',' ',' ',' ',
' ','N','i','k','i','t','a'
};
const int l[5]={5,4,5,3,6};
inline bool check(int x){
bool flag;
for(int i=0;i<5;i++){
flag=1;if(x+l[i]-1<=n){
for(int j=1;j<=l[i];j++)if(s[x+j-1]!=c[i][j])flag=0;
if(flag)return 1;
}
}
return 0;
}
int main()
{
scanf("%s",s+1);
int cnt=0;n=strlen(s+1);
for(int i=1;i<=n;i++)if(check(i))cnt++;
puts(cnt==1?"YES":"NO");
}
B:
维护下a和b的前缀和,枚举两个交界点就可以了
代码比前一题好写到不知道哪里去了,但是很容易fst啊特别是交界点的特判(还好我打的是vp否则也跟着GG了
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
using namespace std;
inline int read(){
int k=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
return k*f;
}
char a[100010];
int s[100010],t[100010];
int main()
{
scanf("%s",a+1);int n=strlen(a+1);
for(int i=1;i<=n;i++){
s[i]=s[i-1];t[i]=t[i-1];
if(a[i]=='a')s[i]++;else t[i]++;
}
int ans=0;
for(int i=1;i<=n;i++)
for(int j=i;j<=n+1;j++){
int p=s[i-1]+s[n]-s[j-1]+t[j-1]-t[i-1];//这里哪些要-1哪些不用就是fst的主要原因辣
ans=max(ans,p);
}
printf("%d",ans);
}
C:
考虑到每个坦克只会移动到相邻两格,所以我们直接按照偶数格奇数格偶数格的顺序扫过去炸一下就好了(这样次数是最少的)
证明:显然成立(因为移动到相邻两格)
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
using namespace std;
inline int read(){
int k=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
return k*f;
}
int main()
{
int n=read();
printf("%d\n",n/2*2+(n-n/2));
for(int i=2;i<=n;i+=2)printf("%d ",i);
for(int i=1;i<=n;i+=2)printf("%d ",i);
for(int i=2;i<=n;i+=2)printf("%d ",i);
}
D:
大力BFS,我们把每一个点记录一下走到的最少步数,然后向四个方向不断更新节点,但是要保证每个点只会进队一次,否则会MLE
时间复杂度
O(我也不知道到底是多少)
然后就过了23333???
后来看了一下官方题解做法很神奇(我看不懂),不过底下的讨论也给出了很多的不同BFS做法。。。亦可赛艇
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
#define pa pair<int,int>
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline int read(){
int k=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
return k*f;
}
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};
int n,m,k,sx,sy,tx,ty;
char s[1010][1010];
int dist[1010][1010];
bool vis[1010][1010];
queue<pa>q;
int main()
{
memset(dist,-1,sizeof dist);
n=read();m=read();k=read();
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
sx=read();sy=read();q.push(mp(sx,sy));dist[sx][sy]=0;vis[sx][sy]=1;
while(!q.empty()){
pa now=q.front();q.pop();
for(int i=0;i<4;i++){
int x=now.fi+dx[i],y=now.se+dy[i];int cnt=1;
while(x>0&&x<=n&&y>0&&y<=m&&s[x][y]!='#'&&(dist[x][y]==-1||dist[x][y]==dist[now.fi][now.se]+1)&&cnt<=k){
dist[x][y]=dist[now.fi][now.se]+1;
if(!vis[x][y])q.push(pa(x,y));vis[x][y]=1;
cnt++;x+=dx[i];y+=dy[i];
}
}
}
int tx=read(),ty=read();
printf("%d",dist[tx][ty]);
return 0;
}
E:
线段树维护dfs序区间修改裸题
打区间翻转(就是xor)的lazy标记就可以了
这种题显然是数据结构啊,看到这种子树的修改和询问操作想都不用想直接上就可以了
时间复杂度
O(nlogn)
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
using namespace std;
inline int read(){
int k=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
return k*f;
}
int nedge=0,p[200010],nex[200010],head[200010];
int n,t[800010],add[800010],lt[800010],rt[800010];
int sx[200010],cnt=0,a[200010],s[200010],xs[200010];
inline void addedge(int a,int b){
p[++nedge]=b;nex[nedge]=head[a];head[a]=nedge;
}
inline void dfs(int x){
sx[x]=++cnt;xs[cnt]=x;s[x]=1;
for(int k=head[x];k;k=nex[k]){
dfs(p[k]);s[x]+=s[p[k]];
}
}
inline void pushdown(int nod){
if(!add[nod])return;
if(lt[nod]!=rt[nod]){
add[nod*2]^=1;add[nod*2+1]^=1;
t[nod*2]=rt[nod*2]-lt[nod*2]+1-t[nod*2];
t[nod*2+1]=rt[nod*2+1]-lt[nod*2+1]+1-t[nod*2+1];
}add[nod]=0;
}
inline void build(int l,int r,int nod){
lt[nod]=l;rt[nod]=r;
if(l==r){t[nod]=a[xs[l]];return;}
int mid=l+r>>1;
build(l,mid,nod*2);build(mid+1,r,nod*2+1);
t[nod]=t[nod*2]+t[nod*2+1];
}
inline void xg(int i,int j,int nod){
pushdown(nod);
if(lt[nod]>=i&&rt[nod]<=j){
t[nod]=rt[nod]-lt[nod]+1-t[nod];add[nod]=1;
return;
}
int mid=lt[nod]+rt[nod]>>1;
if(i<=mid)xg(i,j,nod*2);
if(j>mid)xg(i,j,nod*2+1);
t[nod]=t[nod*2]+t[nod*2+1];
}
inline int ssum(int i,int j,int nod){
pushdown(nod);
if(lt[nod]>=i&&rt[nod]<=j)return t[nod];
int mid=lt[nod]+rt[nod]>>1,ans=0;
if(i<=mid)ans+=ssum(i,j,nod*2);
if(j>mid)ans+=ssum(i,j,nod*2+1);
return ans;
}
int main()
{
n=read();
for(int i=2;i<=n;i++){
int x=read();
addedge(x,i);
}
dfs(1);
for(int i=1;i<=n;i++)a[i]=read();
build(1,n,1);
int m=read();
for(int i=1;i<=m;i++){
char c[5];scanf("%s",c);int x=read();
if(c[0]=='g')printf("%d\n",ssum(sx[x],sx[x]+s[x]-1,1));
else xg(sx[x],sx[x]+s[x]-1,1);
}
return 0;
}
F:
最后一题有点意思,我并没有在两个小时的时间内写出来
%%%wzp(就是那位AKdalao)考场直接切掉了orz
这题是怎么被wzp想到莫队的啊QAQ
我们考虑对这整个数列做前缀和(种类和数量,具体看代码),记为
s[i]
。那么对于一个区间
[l,r]
,在该区间内的
i
对答案的贡献就是在区间内的前缀和=
那么我们对于每个询问都这么暴力地去算,然后用莫队搞一下就好了
关键不在莫队,因为数值范围很大,所以我们需要对
s[i]
进行离散化处理,所以会麻烦很多。。。
时间复杂度貌似是
O(nn√+nlogn)
那就贴代码吧(WA了n发才过的QAQ)
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
#define int long long
using namespace std;
inline int read(){
int k=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
return k*f;
}
struct ppap{int x,y,p,w;}qq[100010];
inline bool cmp(ppap a,ppap b){return a.p==b.p?a.y<b.y:a.p<b.p;}
int a[100010],s[100010],b[100010],q[100010],h[100010],now,n,k,ans[100010];
inline void add1(int x){
if(h[x])now+=a[h[x]];a[s[x]]++;
}
inline void add2(int x){
if(q[x])now+=a[q[x]];a[s[x]]++;
}
inline void jian1(int x){
a[s[x]]--;if(h[x])now-=a[h[x]];
}
inline void jian2(int x){
a[s[x]]--;if(q[x])now-=a[q[x]];
}
signed main()
{
n=read();k=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++){
int x=read();
s[i]=s[i-1]+((a[i]==1)?x:-x);//前缀和:第一种书+,第二种书-
b[i]=s[i];
}
sort(b+1,b+n+1);int cnt=unique(b+1,b+n+1)-b-1;
int K=lower_bound(b+1,b+cnt+1,k)-b;if(b[K]!=k)K=0;
for(int i=1;i<=n;i++){
q[i]=lower_bound(b+1,b+cnt+1,s[i]-k)-b;//记s[i]-k
if(b[q[i]]!=s[i]-k)q[i]=0;
h[i]=lower_bound(b+1,b+cnt+1,s[i]+k)-b;//记s[i]+k
if(b[h[i]]!=s[i]+k)h[i]=0;
}
for(int i=1;i<=n;i++)s[i]=lower_bound(b+1,b+cnt+1,s[i])-b;
h[0]=K;//以上全为离散化
int Q=read();int P=sqrt(n);
for(int i=1;i<=Q;i++){
qq[i].x=read();qq[i].y=read();
qq[i].p=(qq[i].x-1)/P+1;qq[i].w=i;
}
sort(qq+1,qq+Q+1,cmp);
memset(a,0,sizeof a);
int l=1,r=0;now=0;
for(int i=1;i<=Q;i++){
while(l<qq[i].x)jian1(l++);
while(l>qq[i].x)add1(--l);
while(r<qq[i].y)add2(++r);
while(r>qq[i].y)jian2(r--);
ans[qq[i].w]=now+a[h[l-1]];//因为是前缀和,有些以l为开头的合法区间要用l-1来算
}
for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]);
return 0;
}
总结:
这场题目算是比较水的了(至少对于前5题来说),题目也就简单的思路题和算法模板题了,F题比较有趣,考验思维和代码能力
(按照前5题的水平)关键是noip不会出这种大水题吧。。。
看来我还是得去做更难的题了