今天总是看错题怎么办???
T1:
题目大意:维护一个序列,每个元素是一个栈,支持以下操作:
1.格式a v,表示将v复制一份并往栈顶插入一个元素i后放入元素i中.
2.格式b v,表示将v复制一份,查询并删除栈顶后放入元素i中.
3.格式c v w,表示将v复制一份放入元素i中,并查询元素w和v中有多少个相同的元素.
注:1<=序列元素个数n<=300000.
考场想了好久,然后突然发现自己将题目理解错了...
然后理解正确之后想了一会就得出正解...
就是构造一颗树,每个树都存一个栈,其中每个节点k的父亲是k去掉栈顶元素.
那么我们就可以以空栈为根节点,建立一颗树型结构,维护可持久化栈.
然后两个节点的相同元素数量就是他们的lca的元素数量了.
考场AC如下:
#include<bits/stdc++.h>
using namespace std;
#define ACF inline void
char rc(){
char c=getchar();
for (;c<'a'||c>'c';c=getchar());
return c;
}
int ri(){
int x=0;
char c=getchar();
for (;c<'0'||c>'9';c=getchar());
for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
return x;
}
const int N=300000;
const int M=19;
struct Node{
int top,num,grand[M+1];
}nod[N+5];
struct Change{
int num,v,w,ans;
}c[N+5];
int n,a[N+5],top;
ACF into(){
char cc;
int w,v;
n=ri();
nod[++top].num=1;a[0]=1;
for (int i=1;i<=n;i++){
cc=rc();
switch (cc){
case 'a':v=ri();
c[i].num=1;
c[i].v=v;
a[i]=++top;
nod[top].top=i;
nod[top].num=nod[a[v]].num+1;
nod[top].grand[0]=a[v];
for (int i=1;i<=M;i++)
nod[top].grand[i]=nod[nod[top].grand[i-1]].grand[i-1];
break;
case 'b':v=ri();
if (a[v]==1) {
a[i]=1;
c[i].num=2;
c[i].v=v;
c[i].ans=0;
}else{
c[i].num=2;
c[i].v=v;
c[i].ans=nod[a[v]].top;
a[i]=nod[a[v]].grand[0];
}
break;
case 'c':v=ri(),w=ri();
c[i].num=3;
c[i].v=v,c[i].w=w;
a[i]=a[v];
break;
}
}
}
int lca(int u,int v){
if (nod[u].num<nod[v].num) swap(u,v);
for (int i=M;i>=0;i--)
if (nod[nod[u].grand[i]].num>=nod[v].num) u=nod[u].grand[i];
for (int i=M;i>=0;i--)
if (nod[u].grand[i]!=nod[v].grand[i]) u=nod[u].grand[i],v=nod[v].grand[i];
if (u==v) return nod[u].num-1;
else return nod[nod[u].grand[0]].num-1;
}
ACF work(){
for (int i=1;i<=n;i++)
if (c[i].num==3) c[i].ans=lca(a[c[i].v],a[c[i].w]);
}
ACF outo(){
for (int i=1;i<=n;i++)
if (c[i].num!=1) printf("%d\n",c[i].ans);
}
int main(){
freopen("stogovi.in","r",stdin);
freopen("stogovi.out","w",stdout);
into();
work();
outo();
return 0;
}
T2:
考场上什么都没想只想写暴力...
题目大意:
S(1)=B.
S(n)=S(n-1)+B+reverse(flip(S(n-1))).
其中reverse表示翻转字符串,flip表示将字符串中的B改成D,D改成B.
现在回答T个询问,每个询问[l,r]表示询问S(2^1000)中区间[l,r]中的B的数量.
注:1<=T<=1000 1<=L<=R<=10^18.
考场L<=R<=10^6的30分暴力分拿到了...
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ACF inline void
const int N=1000000;
int a[N*2+10],n,l,r;
bool b[N*2+10];
ACF start(){
b[1]=1;n=1;
for (;n<=1000000;){
b[n+1]='B';
for (int i=1;i<=n;i++)
b[n+i+1]=!b[n-i+1];
n<<=1;n++;
}
for (int i=1;i<=n;i++)
a[i]=a[i-1]+(b[i]?1:0);
}
ACF into(){
scanf("%d%d",&l,&r);
}
ACF work(){
}
ACF outo(){
printf("%d\n",a[r]-a[l-1]);
}
int main(){
freopen("bd.in","r",stdin);
freopen("bd.out","w",stdout);
int T;
start();
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}
然后这道题其实正解也不难,因为到其实S(n)和S(n-1)的前半部分其实是一样的,所以其实我们只需要处理到S(62)就行了.
那么暴力处理出S(1~68)的B的数量.
然后我们考虑将查询[l,r]拆成查询[1,r]-[1,l-1].
然后用f(n)表示查询[1,n]的答案.
那么很明显的是我们刻意把一段区间[1,n]的答案这样拆分:
那么剩余部分的答案肯定是已处理出来的部分的一部分,这一部分又可以通过已预处理的部分-f(去掉剩余部分的前面一段)求出.
用图可以这样理解:
然后就可以证明时间复杂度是O(log(n))的.
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ACF inline void
typedef long long LL;
LL S[63],l,r,le[63];
int n;
ACF start(){
int r=1;
S[1]=1LL;le[1]=1LL;
for (int i=2;i<=62;i++)
le[i]=le[i-1]*2+1,S[i]=1+le[i-1]; //S[i]=S[i-1]+1+le[i-1]-S[i-1]=1+le[i-1]
}
ACF into(){
scanf("%lld%lld",&l,&r);
}
ACF work(){
}
LL f(LL R,int k=62){
if (!R) return 0;
if (le[k]>=R) return f(R,k-1);
else return f(R-le[k]-1,k-1)+S[k]+1-(R>le[k]+1+le[k]/2);
}
ACF outo(){
printf("%lld\n",f(r)-f(l-1));
}
int main(){
freopen("bd.in","r",stdin);
freopen("bd.out","w",stdout);
int T;
start();
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}
T3:
题目大意:给出一条数轴,每个整数点上有一座城市,有N辆卡车,每辆卡车有一套序列k来叙述,表示卡车的初始点、转折点和终止点.现在有m个询问,每个询问两个数,表示查询这两个数迎面相撞次数,注意,当有一辆车掉头获正出发或结束时不算.
注:n<=10^5 1<=k的和<=2*10^5.
这道题一看就不是代码很短的题,在考试的时候没写...
正解就是考虑暴力,对于每两辆卡车i,j,我们在O(Ki+Kj)的时间内回答询问.
那么我们把这两辆车的所有转折点搞下来,然后维护两个指针i,j,每次转移的时候i和j都一个偏移到转折点或两个都偏移到转折点,就可以每一段都快速判断,然后开心地AC这道题了.
AC代码今天没码好,下次有时间了补.