A
设 f [ i ] [ j ] f[i][j] f[i][j] 表示 a = i , b = j a=i,b=j a=i,b=j 时必胜还是必败,然后做个 d p dp dp 即可。
由于只有当 j = 1 j=1 j=1 时, i i i 可能很大,但容易发现当 i > n i>\sqrt n i>n 时,两个人都只能操作 a a a,因为一操作 b b b 就超过 n n n 了,所以判断一下 n − a n-a n−a 的奇偶性即可。
当 j > 1 j>1 j>1 时, i i i 的枚举范围很小,直接暴力枚举转移即可。
如果 i j > n i^j>n ij>n,那么 f [ i ] [ j ] f[i][j] f[i][j] 就是必胜态。由于bool数组的初始状态为false,所以方便一点在代码中定义false为必胜态。
代码如下:
#include <cstdio>
#include <cmath>
#define ll long long
int n,m;
bool f[40010][40],v[40010][40];//v[i][j]记录i^j是否小于等于n,是则为true
ll ksm(int x,int y)
{
ll re=1;
while(y)
{
if(y&1)re=re*x;
x=x*x;y>>=1;
}
return re;
}
void dp()
{
for(int i=2;i<=40000;i++)
for(ll j=1,tot=i;tot<=n&&j<=30;j++,tot*=i)v[i][j]=true;
f[(int)sqrt(n)+1][1]=(n-(int)sqrt(n))%2;
for(int j=30;j>=1;j--)
for(int i=sqrt(n);i>=2;i--)
f[i][j]=(v[i][j]&(!f[i+1][j])&(!f[i][j+1]));
}
int main()
{
scanf("%d %d",&n,&m); dp();
while(m--)
{
int x,y;
scanf("%d %d",&x,&y);
if(ksm(x,y+1)>n&&ksm(x+1,y)>n)printf("No\n");//假如无论怎么走都大于n,则必败
else if(y==1&&x>sqrt(n))printf((n-x)%2==1?"Yes\n":"No\n");
else if(!f[x][y])printf("Yes\n");
else printf("No\n");
}
}
B
考虑一次看每一个数列第 k 3 \dfrac k 3 3k 小的位置,该位置最小的那个数列的前 k 3 \dfrac k 3 3k 个数,一定都是前 k k k 小的,于是可以使 k − = k 3 k-=\dfrac k 3 k−=3k,然后这个数列下次就从 k 3 + 1 \dfrac k 3+1 3k+1 开始算。
这样每次减少 1 3 \dfrac 1 3 31,大约 3 log 3 k 3\log_3k 3log3k 次查询即可解决。
代码如下:
#include <cstdio>
#include <cstring>
#include "kth.h"
#include <algorithm>
using namespace std;
int get_a(int p);
int get_b(int p);
int get_c(int p);
int get(int id,int x){
if(id==0)return get_a(x-1);
if(id==1)return get_b(x-1);
if(id==2)return get_c(x-1);
}
struct par{int id,x,y;};
bool cmp(par x,par y){return x.x<y.x;}
int query_kth(int na,int nb,int nc,int k){
static int n[3],last[3],ans;
static vector<par> d;
n[0]=na;n[1]=nb;n[2]=nc;
for(int i=0;i<3;i++)last[i]=0;
//事实上,由于当询问超出n时会返回2^31-1,所以其实不用判那么多边界。
while(k){
d.clear();
int tot=0;
for(int i=0;i<3;i++)if(last[i]<n[i])tot++;
tot=max(k/tot,1);
for(int i=0;i<3;i++)if(last[i]<n[i]){
int next=min(last[i]+tot,n[i]);
par p=(par){i,get(i,next),next-last[i]};
d.push_back(p);
}
sort(d.begin(),d.end(),cmp);
par p=*d.begin();
k-=p.y;
ans=p.x;
last[p.id]+=p.y;
}
return ans;
}
//上面是AC代码,下面是用来本地测试的,本地测试时需要注释掉上面的kth.h头文件
#define maxn 100010
int a[maxn],b[maxn],c[maxn];
int get_a(int p){return a[p];}
int get_b(int p){return b[p];}
int get_c(int p){return c[p];}
int main()
{
int na,nb,nc,k;
scanf("%d %d %d %d",&na,&nb,&nc,&k);
for(int i=0;i<na;i++)scanf("%d",&a[i]);
for(int i=0;i<nb;i++)scanf("%d",&b[i]);
for(int i=0;i<nc;i++)scanf("%d",&c[i]);
printf("%d",query_kth(na,nb,nc,k));
}
当然也有一些很骚的写法,像这样:
#include "kth.h"
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 100010
int na,nb,nc,k,ans,nowa=1,nowb=1,nowc=1;
int a[maxn],b[maxn],c[maxn];
struct par{int x,y;};
par s[10]; int t;
bool cmp(par x,par y){return x.x<y.x;}
inline char cn()
{
static char buf[1000010],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
void read(int &x)
{
x=0;int f1=1;char ch=cn();
while(ch<'0'||ch>'9'){if(ch=='-')f1=-1;ch=cn();}
while(ch>='0'&&ch<='9')x=x*10+(ch-'0'),ch=cn();
x*=f1;
}
int next()
{
t=0;
if(nowa<=na)s[++t]=(par){a[nowa],1};
if(nowb<=nb)s[++t]=(par){b[nowb],2};
if(nowc<=nc)s[++t]=(par){c[nowc],3};
sort(s+1,s+t+1,cmp);
if(s[1].y==1)return a[nowa++];
if(s[1].y==2)return b[nowb++];
if(s[1].y==3)return c[nowc++];
}
void ungetc(int x){ungetc(x,stdin);}
int init()
{
read(na);read(nb);read(nc);read(k);
for(int i=1;i<=na;i++)read(a[i]);
for(int i=1;i<=nb;i++)read(b[i]);
for(int i=1;i<=nc;i++)read(c[i]);
ungetc(10);ungetc('1');ungetc(10);ungetc(10);ungetc(10);
ungetc('1');ungetc(' ');ungetc('1');ungetc(' ');ungetc('0');ungetc(' ');ungetc('0');
while(k--)ans=next();
return 1;
}
int wulala=init();
int query_kth(int n_a,int n_b,int n_c,int k){return ans;}
即提前将输入读进来,然后直接用手写堆求解,这样一次询问都不用……
然后要注意再往输入流里塞一个假数据骗骗评测机,别让他输入RE了或什么的。
不过其实不推崇这个做法,毕竟就没有思维难度和收获了……
C
以前写代码又臭又长,还AC不了,居然这篇题解当时还能不知廉耻的占据到百度搜索时的第一位,真是惭愧,现在补了这题先谢个罪……
思路大体是没变的,有些地方有变动,再重新讲一次。
先考虑暴力,前 k k k 大肯定要用堆来求,在堆里存现在的所有路径的末尾,每次取出最小的然后将所有后继路径都加进去,这样是 O ( n k log ( n k ) ) O(nk\log (nk)) O(nklog(nk)) 的。
考虑优化,简单讨论一下可以将 a i , b i , c i a_i,b_i,c_i ai,bi,ci 这三个点之间的路径拆成两条链,找到一条链上的 w w w 最小的点,当有一条路径从 i i i 往外走时,只需要考虑走到这两条链上的最小点上即可。
但是这不是说路径上其他点不需要考虑了,显然也是要的,假设 x x x 所在的链为 ( u , v ) (u,v) (u,v),那么从 x x x 往外走时,不单单加入 x x x 到上面说的两条链上的最小点这两条路径,假设 x x x 这条路径的上一个点为 p p p,你还需要加入 c c c 到 ( u , x ) , ( v , x ) (u,x),(v,x) (u,x),(v,x) 这两条链的最小点这两条路径,这样就可以将所有路径考虑上了。(注意,这里 ( u , x ) , ( v , x ) (u,x),(v,x) (u,x),(v,x) 两条链不包括 x x x 本身)。
关于如何将
a
i
,
b
i
,
c
i
a_i,b_i,c_i
ai,bi,ci 三点之间的路径拆成两条链:为了方便,设这三个点为
x
,
y
,
z
x,y,z
x,y,z,那么其实主要只有这两种情况:
注意到,交换
x
,
y
,
z
x,y,z
x,y,z 的顺序,一定存在一种顺序可以使
lca
(
x
,
z
)
=
lca
(
y
,
z
)
\text{lca}(x,z)=\text{lca}(y,z)
lca(x,z)=lca(y,z),此时三点关系如上图,
z
z
z 可能是
x
,
y
x,y
x,y 的祖先,但是不是其实都无所谓。
两图的不同在于 x , y x,y x,y 之间的关系,若 y y y 是 x x x 的祖先,那么其实只需要分解成 ( x , z ) (x,z) (x,z) 这一条链即可,否则分解为 ( x , y ) , ( f a [ lca ( x , y ) ] , z ) (x,y),(fa[\text{lca}(x,y)],z) (x,y),(fa[lca(x,y)],z)。
以及一些卡空间上的细节:
- 倍增空间太大,要用树链剖分(这个官方题解就有说)
- 线段树我用的zkw,只需要大约 1 0 6 10^6 106 个int
- 存边用邻接表比用vector好,既快不少又能省大概 7 M 7M 7M 空间
- 有一些显然不优的路径就不放到堆里面了
- 原来有一些数组用一次就不用了——但事实上后面你可以循环利用,虽然变量名会有些别扭
- 这题时间充裕,可以考虑在一些地方时间换空间,但这份代码里实际上没有使用。
代码如下:
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 500010
#define cn getchar
void read(int &x){
x=0;int f1=1;char ch=cn();
while(ch<'0'||ch>'9'){if(ch=='-')f1=-1;ch=cn();}
while(ch>='0'&&ch<='9')x=x*10+(ch-'0'),ch=cn(); x*=f1;
}
void write(int x){
static char op[110];
static int t;t=0;
if(!x)putchar('0');
while(x)op[++t]=x%10+'0',x/=10;
while(t)putchar(op[t--]);
}
int n,k,w[maxn],wmax=0;
struct edge{int y,next;}e[maxn];
int first[maxn],len=0;
void buildroad(int x,int y){e[++len]=(edge){y,first[x]};first[x]=len;}
int fa[maxn],dep[maxn],sz[maxn],mson[maxn];
void dfs1(int x){
sz[x]=1;
for(int i=first[x];i;i=e[i].next){
int y=e[i].y;dep[y]=dep[x]+1;dfs1(y);
if(sz[y]>sz[mson[x]])mson[x]=y;
sz[x]+=sz[y];
}
}
int id[maxn],idtot=0,top[maxn],mi[maxn];
void dfs2(int x,int tp){
top[x]=tp;id[x]=++idtot;sz[idtot]=x;
if(tp==x)mi[x]=x;
else mi[x]=(w[x]<w[mi[fa[x]]]?x:mi[fa[x]]);
if(mson[x])dfs2(mson[x],tp);
for(int i=first[x];i;i=e[i].next)
if(e[i].y!=mson[x])dfs2(e[i].y,e[i].y);
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]])swap(x,y);
y=fa[top[y]];
}
return dep[x]>dep[y]?y:x;
}
struct zkw{
int mi[1048580],m;
void buildtree(int N){
for(m=1;m+2<=N;m<<=1);
for(int i=m+1;i<=m+n;i++)mi[i]=sz[i-m];
for(int i=m;i>=1;i--)mi[i]=(w[mi[i<<1]]<w[mi[i<<1|1]]?mi[i<<1]:mi[i<<1|1]);
}
int ask(int x,int y){
int re=0;
for(x=x+m-1,y=y+m+1;x^y^1;x>>=1,y>>=1){
if(~x&1)re=(w[re]>w[mi[x^1]]?mi[x^1]:re);
if( y&1)re=(w[re]>w[mi[y^1]]?mi[y^1]:re);
}
return re;
}
}tr;
struct node{int mi,new_x;};
node get_min(int x,int y){
if(x==y)return (node){0,0};
int px=x,last,new_x,re=0;
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]){
if(x==px){x=new_x=fa[x];continue;}
swap(x,y);
}
if(w[mi[y]]<w[re])re=mi[y];
last=top[y];y=fa[last];
}
if(dep[x]>dep[y]){
if(x==px)x=new_x=fa[x];
swap(x,y);
}
if(x==px){
if(x==y){new_x=last;return (node){re,new_x};}
else x=new_x=mson[x];
}
last=tr.ask(id[x],id[y]);
if(w[last]<w[re])re=last;
return (node){re,new_x};
}
struct chain{int x,y,mi;}chain1[maxn],chain2[maxn];
chain make_chain(int x,int y,bool type){
node p=get_min(x,y);
if(type&&w[p.mi]>w[x])p.mi=x;
if(type)p.new_x=x;
return (chain){p.new_x,y,p.mi};
}
struct par{
int x,z;chain be;
bool operator <(const par &B)const{return z>B.z;}
};
priority_queue<par> q;
void qadd(par x){if(n<k||x.z<wmax)q.push(x);}
int main()
{
read(n);read(k);w[0]=1e9;
for(int i=1;i<=n;i++)read(w[i]),wmax=max(wmax,w[i]);
for(int i=2;i<=n;i++)read(fa[i]),buildroad(fa[i],i);
dfs1(1);dfs2(1,1);tr.buildtree(n);
for(int i=1,x,y,z,xy,yz,xz;i<=n;i++){
read(x);read(y);read(z);
xy=lca(x,y);yz=lca(y,z);xz=lca(x,z);
if(xy==xz)swap(x,z),swap(xy,yz);
if(xy==yz)swap(y,z),swap(xy,xz);
if(xy==x||xy==y)chain1[i]=make_chain(xy==x?y:x,z,1);
else{
chain1[i]=make_chain(x,y,1);
chain2[i]=make_chain(xy,z,0);
}
}
for(int i=1;i<=n;i++)q.push((par){i,w[i],(chain){0,0,0}});
for(int nowk=1;nowk<=k;nowk++){
par x=q.top();q.pop();
write(x.z);puts("");
if(x.be.x!=0){
node p;
if(x.be.x!=x.x){
p=get_min(x.x,x.be.x);
qadd((par){p.mi,x.z-w[x.x]+w[p.mi],(chain){p.new_x,x.be.x,p.mi}});
}
if(x.be.y!=x.x){
p=get_min(x.x,x.be.y);
qadd((par){p.mi,x.z-w[x.x]+w[p.mi],(chain){p.new_x,x.be.y,p.mi}});
}
}
qadd((par){chain1[x.x].mi,x.z+w[chain1[x.x].mi],chain1[x.x]});
if(chain2[x.x].x!=0)qadd((par){chain2[x.x].mi,x.z+w[chain2[x.x].mi],chain2[x.x]});
}
}