在RP守恒定律的持续作用下,
不出所料,这场稍炸
还有10分钟就是下一场了,但愿继续守恒?
改题太慢了,连写博的时间都没有了
然而最后还是在吃饭前彻彻底底改出来了
的确是个菜鸡
所以今天的题解只能先咕了
晚上加油
1012upd:RP守恒,breaked
下一篇反思再说。
但是这套题有不少要反思的。
T1打的是错解但是用了各种技巧让小点必然正确大点有概率正确。
T2沉迷测试点分治为了那4分9分的玩意浪费的大量的时间,最后只调出来了暴力,很多子任务都没有调,也就没有分。
但是其实T2已经想到伪正解了(会被卡常)但是感觉会很难码而且时间不多就扔了(其实也就是不会主席树查前驱后继)
其实就算把分治打满也就55分。明显因小失大。
但是因为大量的时间浪费在这上面了,T3我只留了25分钟左右。
快速的码完暴力,快速的想到75分做法,但是没有时间打了。
不要沉迷测试点分治,要在相同的时间内得尽量多的分数。
一定要给每一道题留下充足的时间,不要想到高分算法但是没时间打。
注意分数与时间的关系。
T1:d
不满足三分性质。但是可以大概乱打一下。这样就能保证小的子任务不失分了。

1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int read(){ 5 register int p=0;register char ch=getchar(); 6 while(ch<'0'||ch>'9')ch=getchar(); 7 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 8 return p; 9 } 10 struct rec{int x,y,id;}rx[100005],ry[100005]; 11 bool X(rec a,rec b){return a.x<b.x||(a.x==b.x&&a.y<b.y);} 12 bool Y(rec a,rec b){return a.y<b.y||(a.y==b.y&&a.x<b.x);} 13 int n,m;long long ans;char ald[100005]; 14 long long chk(int x){ 15 int res=m-x;long long fy;//printf("%d ",x); 16 for(int i=1;i<=x;++i)ald[rx[i].id]=1; 17 for(int i=1;i<=n;++i)if(!ald[ry[i].id]&&!res){fy=ry[i].y;break;}else if(!ald[ry[i].id])res--; 18 for(int i=1;i<=x;++i)ald[rx[i].id]=0;//printf("%lld ",fy); 19 if(rx[x+1].x*fy>ans)ans=rx[x+1].x*fy;return rx[x+1].x*fy; 20 } 21 int main(){//freopen("d2.in","r",stdin);freopen("my.out","w",stdout); 22 int t=read(); 23 while(t--){ 24 n=read(),m=read(); 25 for(int i=1;i<=n;++i)rx[i].x=read(),rx[i].y=read(),rx[i].id=i,ry[i]=rx[i]; 26 sort(rx+1,rx+1+n,X);sort(ry+1,ry+1+n,Y); 27 int l=0,r=m;ans=0; 28 if(n-1==m){ 29 for(int i=1;i<=n;++i)if(1ll*rx[i].x*rx[i].y>ans)ans=1ll*rx[i].x*rx[i].y; 30 goto A; 31 } 32 if(rx[1].x==rx[n].x){ans=1ll*ry[m+1].y*rx[1].x;goto A;} 33 while(l<r-500){ 34 long long ansl=chk(l+r>>1),ansr=chk((l+r>>1)+1); 35 if(ansl<ansr)l=l+r>>1; 36 else if(ansr<ansl)r=(l+r>>1)+1; 37 else{break; 38 ansl=chk(l+(r-l)/3),ansr=chk(l+(r-l)*2/3); 39 if(ansl<ansr)l=l+(r-l)/3; 40 else if(ansr>ansl)r=l+(r-l)*2/3; 41 else break; 42 } 43 } 44 for(;l<=r;++l)chk(l); 45 A: printf("%lld ",ans); 46 } 47 }
思路其实差不多,当然费用与“删除的x最小的矩形个数”相关。
关键是删除x最小的矩形的同时有一些y最小的也同时被删除了。
那么还要继续删掉几个,怎么知道要删哪些呢?
从大到小枚举x最小的矩形删除个数,这样的话每次会加入一个矩形。
然后同时,你可以多删除一个矩形,删除y最小的一个。
随便一个堆就可以满足这种“单个加入,取出并删除最小”的要求了

1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 using namespace std; 5 int read(){ 6 register int p=0;register char ch=getchar(); 7 while(ch<'0'||ch>'9')ch=getchar(); 8 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 9 return p; 10 } 11 struct rec{ 12 int x,y; 13 friend bool operator<(rec a,rec b){ 14 return a.y>b.y; 15 } 16 }rx[100005]; 17 priority_queue<rec>q; 18 bool X(rec a,rec b){return a.x<b.x||(a.x==b.x&&a.y<b.y);} 19 int n,m;long long ans; 20 int main(){//freopen("d2.in","r",stdin);freopen("my.out","w",stdout); 21 int t=read(); 22 while(t--){ 23 n=read(),m=read();ans=0;while(!q.empty())q.pop(); 24 for(int i=1;i<=n;++i)rx[i].x=read(),rx[i].y=read(); 25 sort(rx+1,rx+1+n,X); 26 for(int i=m+1;i<=n;++i)q.push(rx[i]); 27 for(int i=m;~i;--i)ans=max(ans,1ll*rx[i+1].x*q.top().y),q.push(rx[i]),q.pop(); 28 printf("%lld ",ans); 29 } 30 }
思路积累:
- 转换题意
- 正难则反
- 根据要求选择数据结构
T2:e
纪念一下愚蠢的测试点分治的冗长的调了两小时还没什么分的代码。

1 //subtask1-2:violence 2 //subtask3:ans=|a-r| 3 //subtask4:ans=|ap-r| 4 //subtask5:tree*10 5 //subtask6:min-1 6 //subtask7-8:2B_balanced_tree? 7 //subtask9:return 0 8 //subtask10:2B_balanced_tree? 9 #include<cstdio> 10 #include<iostream> 11 #include<vector> 12 using namespace std; 13 int read(){ 14 register int p=0;register char ch=getchar(); 15 while(ch<'0'||ch>'9')ch=getchar(); 16 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 17 return p; 18 } 19 int n,q,type,fir[100005],l[200005],to[200005],cnt,a[100005],r[300005],k[300005]; 20 vector<int>v[300005]; 21 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;} 22 int _abs(int x){return x>0?x:-x;} 23 int dep[100005],f[100005][20],ap[100005][20][11],mn[100005][20]; 24 int mxk=0,mxr=0,mxa=0,mna=1234567890; 25 void dfs(int p,int fa){ 26 dep[p]=dep[fa]+1;f[p][0]=fa;if(mxa<=10)ap[p][0][a[p]]=1;mn[p][0]=a[p]; 27 for(int i=1;i<=17;++i){ 28 f[p][i]=f[f[p][i-1]][i-1];mn[p][i]=min(mn[p][i],mn[f[p][i-1]][i-1]); 29 if(mxa<=10)for(int j=1;j<=10;++j)ap[p][i][j]=ap[p][i-1][j]|ap[f[p][i-1]][i-1][j]; 30 } 31 for(int i=fir[p];i;i=l[i])if(to[i]!=fa)dfs(to[i],p); 32 } 33 int lca(int a,int b){ 34 int sub=dep[a]-dep[b]; 35 if(sub<0)sub=-sub,a^=b^=a^=b; 36 for(int i=17;~i;--i)if(sub&1<<i)a=f[a][i];//printf("%d %d ",dep[a],dep[b]); 37 if(a==b)return a; 38 for(int i=17;~i;--i)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];//,printf("in lca:%d %d %d %d ",a,b,f[a][0],f[b][0]); 39 return f[a][0]; 40 } 41 namespace subtask1_2_7_8_10{ 42 int ald[100005]; 43 void main(){ 44 for(int i=1;i<=q;++i){ 45 int LCA=v[i][0],ans=1234567890; 46 for(int j=1;j<k[i];++j)LCA=lca(LCA,v[i][j]);//,printf("LCA:%d ",LCA); 47 for(int j=0;j<k[i];++j){ 48 int p=v[i][j]; 49 while(ald[p]!=i&&p!=LCA)ans=min(ans,_abs(a[p]-r[i])),ald[p]=i,p=f[p][0];//,printf("%d ",p); 50 } 51 printf("%d ",min(ans,_abs(a[LCA]-r[i]))); 52 } 53 } 54 } 55 namespace subtask3{ 56 void main(){ 57 for(int i=1;i<=q;++i)printf("%d ",_abs(a[1]-r[i])); 58 } 59 } 60 namespace subtask4{ 61 void main(){ 62 for(int i=1;i<=q;++i)printf("%d ",_abs(a[v[i][0]]-r[i])); 63 } 64 } 65 namespace subtask5{ 66 void main(){ 67 int alp[11]; 68 for(int i=1;i<=q;++i){ 69 int LCA=v[i][0],ans=11; 70 for(int j=1;j<k[i];++j)LCA=lca(LCA,v[i][j]); 71 for(int j=0;j<k[i];++j){ 72 int sub=dep[v[i][j]]-dep[LCA]+1,p=v[i][j]; 73 for(int i=17;~i;--i)if(sub&1<<i){ 74 for(int j=1;j<=10;++j)if(ap[p][i][j])alp[j]=i; 75 p=f[p][i]; 76 } 77 } 78 for(int j=1;j<=10;++j)if(alp[j]==i)ans=min(ans,_abs(r[i]-j)); 79 printf("%d ",ans); 80 } 81 } 82 } 83 namespace subtask6{ 84 void main(){ 85 for(int i=1;i<=q;++i){ 86 int LCA=v[i][0],ans=1234567890; 87 for(int j=1;j<k[i];++j)LCA=lca(LCA,v[i][j]); 88 for(int j=0;j<k[i];++j){ 89 int sub=dep[v[i][j]]-dep[LCA]+1,p=v[i][j]; 90 for(int i=17;~i;--i)if(sub&1<<i)ans=min(ans,mn[p][i]-1),p=f[p][i]; 91 } 92 printf("%d ",ans); 93 } 94 } 95 } 96 namespace subtask9{ 97 void main(){ 98 return; 99 } 100 } 101 int main(){//freopen("e2.in","r",stdin);freopen("my.out","w",stdout); 102 n=read();q=read();type=read(); 103 for(int i=1;i<=n;++i)mxa=max(mxa,a[i]=read()),mna=min(mna,a[i]); 104 for(int i=1,x,y;i<n;++i)x=read(),y=read(),link(x,y),link(y,x); 105 for(int i=1;i<=q;++i){ 106 mxr=max(mxr,r[i]=read());mxk=max(mxk,k[i]=read()); 107 for(int j=1;j<=k[i];++j)v[i].push_back(read()); 108 } 109 dfs(1,0); 110 if(n<=1000)subtask1_2_7_8_10::main(); 111 else if(mxa==mxa)subtask3::main(); 112 else if(mxk==1)subtask4::main(); 113 else if(mxa<=10&&mxr<=10)subtask5::main(); 114 else if(mxr==1)subtask6::main(); 115 else if(q==0)subtask9::main(); 116 else subtask1_2_7_8_10::main(); 117 }
绝对值。容易转换为查前驱后继。
第一反应:平衡树上树。
第二反应:主席树上树。
哪个好打一些还是比较明显的。在除了区间翻转的情况以外大多数时候主席树都可以替代平衡树,码量也小也好调。
既然问链,那么首先一个草率的思路就是老套路树炼剖分。
但是其实想一下,这道题并不用维护子树,而是只维护链即可,所以这样的话不必按照DFS序建树。
直接让儿子继承父亲的主席树即可。
但是最后我打的还是第一种思路。多一个log。

1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 #define inf 1000000000 5 int read(){ 6 register int p=0;register char ch=getchar(); 7 while(ch<'0'||ch>'9')ch=getchar(); 8 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 9 return p; 10 } 11 int n,q,ans,type,a[100005],res[300005],r,k; 12 int fir[100005],l[200005],to[200005],cnt,f[100005]; 13 int w[10000005],pc,rt[100005],lc[10000005],rc[10000005]; 14 int siz[100005],hson[100005],top[100005],dfn[100005]; 15 int re[100005],dep[100005],tim; 16 void link(int x,int y){l[++cnt]=fir[x];fir[x]=cnt;to[cnt]=y;} 17 //主席树 18 void insert(int &p,int cpy,int l,int r,int v){ 19 p=++pc;w[p]=w[cpy]+1; 20 if(l==r)return; 21 if(v<=l+r>>1)insert(lc[p],lc[cpy],l,l+r>>1,v),rc[p]=rc[cpy]; 22 else insert(rc[p],rc[cpy],(l+r>>1)+1,r,v),lc[p]=lc[cpy]; 23 } 24 void build(){for(int i=1;i<=n;++i)insert(rt[i],rt[i-1],1,inf,a[re[i]]);} 25 int pre_ask(int pl,int pr,int pos,int l,int r){ 26 if(w[pr]==w[pl])return -inf; 27 if(l==r)return l; 28 if((l+r>>1)+1<=pos){int x=pre_ask(rc[pl],rc[pr],pos,(l+r>>1)+1,r);if(x!=-inf)return x;} 29 return pre_ask(lc[pl],lc[pr],pos,l,l+r>>1); 30 } 31 int suc_ask(int pl,int pr,int pos,int l,int r){ 32 if(w[pr]==w[pl])return inf*2; 33 if(l==r)return l; 34 if(pos<=l+r>>1){int x=suc_ask(lc[pl],lc[pr],pos,l,l+r>>1);if(x!=inf*2)return x;} 35 return suc_ask(rc[pl],rc[pr],pos,(l+r>>1)+1,r); 36 } 37 //树链剖分 38 void dfs(int p,int fa){ 39 siz[p]=1;dep[p]=dep[fa]+1;f[p]=fa; 40 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 41 dfs(to[i],p); 42 siz[p]+=siz[to[i]]; 43 if(siz[to[i]]>siz[hson[p]])hson[p]=to[i]; 44 } 45 } 46 void DFS(int p,int fa,int tp){//printf("dfn:%d ",p); 47 dfn[p]=++tim;top[p]=tp;re[tim]=p; 48 if(hson[p])DFS(hson[p],p,tp); 49 for(int i=fir[p];i;i=l[i])if(to[i]!=fa&&to[i]!=hson[p])DFS(to[i],p,to[i]); 50 } 51 int lca(int a,int b){ 52 while(top[a]!=top[b])if(dep[top[a]]>dep[top[b]])a=f[top[a]];else b=f[top[b]]; 53 return dep[a]<dep[b]?a:b; 54 } 55 int upd(int p,int anc,int r){ 56 while(top[p]!=top[anc])ans=min(ans,min(r-pre_ask(rt[dfn[top[p]]-1],rt[dfn[p]],r,1,inf),suc_ask(rt[dfn[top[p]]-1],rt[dfn[p]],r,1,inf)-r)),p=f[top[p]]; 57 ans=min(ans,min(r-pre_ask(rt[dfn[anc]-1],rt[dfn[p]],r,1,inf),suc_ask(rt[dfn[anc]-1],rt[dfn[p]],r,1,inf)-r)); 58 } 59 int main(){//freopen("e2.in","r",stdin);freopen("my.out","w",stdout); 60 n=read();q=read();type=read(); 61 for(int i=1;i<=n;++i)a[i]=read(); 62 for(int i=1,x,y;i<n;++i)x=read(),y=read(),link(x,y),link(y,x); 63 dfs(1,0);DFS(1,0,1);build(); 64 while(q--){ 65 r=read();k=read(); 66 for(int i=1;i<=k;++i)res[i]=(read()-1+ans*type)%n+1; 67 int LCA=res[1]; 68 for(int i=2;i<=k;++i)LCA=lca(LCA,res[i]);//cout<<"LCA:"<<LCA<<endl; 69 ans=1234567890; 70 for(int i=1;i<=k;++i)upd(res[i],LCA,r);//,printf("%d ",ans); 71 printf("%d ",ans); 72 } 73 }
值域1e9过于冗余了。所以需要离散化。
我的做法是在离散化数组最前面加-inf,最后面加一个inf和一个2inf。
因为询问时的r值需要lower_bound,找到的是一个后继,后继的前驱(含自身)不一定就是前驱。
细节比较多。
其实也可以把询问的r值也离散化下来这样就没有那么多锅了,幸亏这题没有把r强制在线。
然而如果打只有一个log的思路其实就不用这么麻烦的离散化了。

1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<map> 5 using namespace std; 6 #define inf 1000000000 7 #define S 100000 8 int read(){ 9 register int p=0;register char ch=getchar(); 10 while(ch<'0'||ch>'9')ch=getchar(); 11 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 12 return p; 13 } 14 int n,q,ans,type,a[100005],res[300005],r,k; 15 int fir[100005],l[200005],to[200005],cnt,f[100005]; 16 int w[10000005],pc,rt[100005],lc[10000005],rc[10000005]; 17 int siz[100005],hson[100005],top[100005],dfn[100005]; 18 int re[100005],dep[100005],tim; 19 int x[100005],numcnt,ref[100005]; 20 map<int,int>M; 21 void link(int x,int y){l[++cnt]=fir[x];fir[x]=cnt;to[cnt]=y;} 22 //主席树 23 void insert(int &p,int cpy,int l,int r,int v){ 24 p=++pc;w[p]=w[cpy]+1; 25 if(l==r)return; 26 if(v<=l+r>>1)insert(lc[p],lc[cpy],l,l+r>>1,v),rc[p]=rc[cpy]; 27 else insert(rc[p],rc[cpy],(l+r>>1)+1,r,v),lc[p]=lc[cpy]; 28 } 29 void build(){for(int i=1;i<=n;++i)insert(rt[i],rt[i-1],0,numcnt,a[re[i]]);} 30 int pre_ask(int pl,int pr,int pos,int l,int r){ 31 if(w[pr]==w[pl])return 0; 32 if(l==r)return l; 33 if((l+r>>1)+1<=pos){int x=pre_ask(rc[pl],rc[pr],pos,(l+r>>1)+1,r);if(x)return x;} 34 return pre_ask(lc[pl],lc[pr],pos,l,l+r>>1); 35 } 36 int suc_ask(int pl,int pr,int pos,int l,int r){ 37 if(w[pr]==w[pl])return numcnt; 38 if(l==r)return l; 39 if(pos<=l+r>>1){int x=suc_ask(lc[pl],lc[pr],pos,l,l+r>>1);if(x!=numcnt)return x;} 40 return suc_ask(rc[pl],rc[pr],pos,(l+r>>1)+1,r); 41 } 42 //树链剖分 43 void dfs(int p,int fa){ 44 siz[p]=1;dep[p]=dep[fa]+1;f[p]=fa; 45 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 46 dfs(to[i],p); 47 siz[p]+=siz[to[i]]; 48 if(siz[to[i]]>siz[hson[p]])hson[p]=to[i]; 49 } 50 } 51 void DFS(int p,int fa,int tp){//printf("dfn:%d ",p); 52 dfn[p]=++tim;top[p]=tp;re[tim]=p; 53 if(hson[p])DFS(hson[p],p,tp); 54 for(int i=fir[p];i;i=l[i])if(to[i]!=fa&&to[i]!=hson[p])DFS(to[i],p,to[i]); 55 } 56 int lca(int a,int b){ 57 while(top[a]!=top[b])if(dep[top[a]]>dep[top[b]])a=f[top[a]];else b=f[top[b]]; 58 return dep[a]<dep[b]?a:b; 59 } 60 int upd(int p,int anc,int r,int rp){ 61 while(top[p]!=top[anc])ans=min(ans,min(rp-ref[pre_ask(rt[dfn[top[p]]-1],rt[dfn[p]],r-1,0,numcnt)],ref[suc_ask(rt[dfn[top[p]]-1],rt[dfn[p]],r,0,numcnt)]-rp)),p=f[top[p]]; 62 ans=min(ans,min(rp-ref[pre_ask(rt[dfn[anc]-1],rt[dfn[p]],r-1,0,numcnt)],ref[suc_ask(rt[dfn[anc]-1],rt[dfn[p]],r,0,numcnt)]-rp)); 63 } 64 main(){//freopen("e2.in","r",stdin);freopen("my.out","w",stdout); 65 n=read();q=read();type=read(); 66 for(int i=1;i<=n;++i)x[i]=a[i]=read(); 67 for(int i=1,xx,y;i<n;++i)xx=read(),y=read(),link(xx,y),link(y,xx); 68 sort(x+1,x+1+n);int P=unique(x+1,x+1+n)-x-1; 69 for(int i=1;i<=P;++i)M[x[i]]=++numcnt,ref[numcnt]=x[i]; 70 numcnt++;ref[numcnt]=inf;M[inf]=numcnt; 71 numcnt++;ref[numcnt]=inf<<1;M[inf<<1]=numcnt; 72 ref[0]=-inf; 73 for(int i=1;i<=n;++i)a[i]=M[a[i]]; 74 dfs(1,0);DFS(1,0,1);build(); 75 while(q--){ 76 r=read();k=read();int R=M[*lower_bound(ref+1,ref+1+numcnt,r)]; 77 for(int i=1;i<=k;++i)res[i]=(read()-1+ans*type)%n+1; 78 int LCA=res[1]; 79 for(int i=2;i<=k;++i)LCA=lca(LCA,res[i]); 80 ans=1234567890; 81 for(int i=1;i<=k;++i)upd(res[i],LCA,R,r); 82 printf("%d ",ans); 83 } 84 }
T3:f
挺好的题,也没有想象中那么难。
首先注意最大逆序对数是100000*99999/2=4999950000,所以会炸int。调半天。。。
和之前《比赛》那道题比较像,异或上[0,2k)上的每一个值的话那么两个数会在最高的不相同位分离。
建出trie,这样我们就能求出在每一位上分离的贡献了。
这样的话其实每一位的贡献都是独立的,可以分别考虑。
最暴力的思路就是直接枚举2k个所有数然后nth_element什么的。

1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 using namespace std; 5 int read(){ 6 register int p=0;register char ch=getchar(); 7 while(ch<'0'||ch>'9')ch=getchar(); 8 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 9 return p; 10 } 11 int a[100005],n,k,p,k1,k2,rt;long long ctb[31][2]; 12 int c[3000005][2],w[3000005],cnt; 13 struct Pair{ 14 int id;long long w; 15 friend bool operator<(Pair a,Pair b){ 16 return a.w<b.w||(a.w==b.w&&a.id<b.id); 17 } 18 }p1[1048576],p2[32769]; 19 void insert(int &p,int v,int al){ 20 if(!p)p=++cnt;w[p]++; 21 if(al==-1)return; 22 insert(c[p][v&1<<al?1:0],v,al-1); 23 ctb[al][v&1<<al?1:0]+=w[c[p][v&1<<al?0:1]]; 24 } 25 int chk(Pair x){ 26 long long lower=0; 27 for(int i=0;i<1<<k1;++i)lower+=lower_bound(p2,p2+(1<<k2),(Pair){x.w-p1[i].w,x.id-(p1[i].id<<k2)})-p2;cerr<<lower<<endl; 28 return lower; 29 } 30 int main(){//freopen("f3.in","r",stdin); 31 n=read(),k=read(),p=read();k1=k>>1;k2=k-k1; 32 for(int i=1;i<=n;++i)insert(rt,read(),k-1); 33 for(int i=0;i<1<<k;++i)p1[i].id=i; 34 for(int i=0;i<1<<k;++i)for(int j=0;j<k;++j)p1[i].w+=ctb[j][i&1<<j?1:0]; 35 nth_element(p1,p1+p-1,p1+(1<<k));printf("%lld %d ",p1[p-1].w,p1[p-1].id); 36 }
对于最大的数据范围k=30,我们要采用那种$meet$ $in$ $middle$的思想。
因为每一位的贡献独立,所以其实可以分开考虑前15位与后15位的贡献。
spj已经给出了提示:第一问好做。
因为回答对任意一问可以得到60%的分,而按照我们已有的结论如果知道第二问的话可以$O(k)$求出第一问。
所以关键就在于怎么求出第一问。
值域可以二分答案,关键就在于如何check。
我们可以暴力扫前15位的全部情况,现在的问题就是求出有多少个后15位与它相加小于二分值。
那么我们只需要把第二维排序,然后lower_bound即可。(也可以双指针扫描,少一个log)
这样的话我们就check出了第一问。

1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 using namespace std; 5 int read(){ 6 register int p=0;register char ch=getchar(); 7 while(ch<'0'||ch>'9')ch=getchar(); 8 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 9 return p; 10 } 11 int a[500005],n,k,p,k1,k2,rt;long long ctb[31][2],w[15000005]; 12 int c[15000005][2],cnt; 13 struct Pair{ 14 long long w;int id; 15 friend bool operator<(Pair a,Pair b){ 16 return a.w<b.w||(a.w==b.w&&a.id<b.id); 17 } 18 }p1[32769],p2[32769]; 19 void insert(int &p,int v,int al){ 20 if(!p)p=++cnt;w[p]++; 21 if(al==-1)return; 22 insert(c[p][v&1<<al?1:0],v,al-1); 23 ctb[al][v&1<<al?1:0]+=w[c[p][v&1<<al?0:1]]; 24 } 25 int chk(Pair x){ 26 long long lower=0; 27 for(int i=0;i<1<<k1;++i)lower+=upper_bound(p2,p2+(1<<k2),(Pair){x.w-p1[i].w,x.id-(p1[i].id<<k2)})-p2;cerr<<lower<<endl; 28 return lower; 29 } 30 int main(){//freopen("f1.in","r",stdin); 31 n=read(),k=read(),p=read();k1=k>>1;k2=k-k1; 32 for(int i=1;i<=n;++i)insert(rt,read(),k-1); 33 for(int i=0;i<1<<k1;++i)for(int j=0;j<k1;++j)p1[i].w+=ctb[j+k2][i&1<<j?1:0]; 34 for(int i=0;i<1<<k1;++i)p1[i].id=i; 35 for(int i=0;i<1<<k2;++i)for(int j=0;j<k2;++j)p2[i].w+=ctb[j][i&1<<j?1:0]; 36 for(int i=0;i<1<<k2;++i)p2[i].id=i; 37 sort(p1,p1+(1<<k1));sort(p2,p2+(1<<k2)); 38 long long l=0,r=1ll*n*(n-1)>>1,ans; 39 while(l<=r)if(chk((Pair){l+r>>1,0})<p)l=(ans=l+r>>1)+1;else r=(l+r>>1)-1; 40 printf("%lld 0 ",ans); 41 }
然后确定第一问后,第二问其实是完全一样的,再次二分。
前15位与后15位的二元组运算是$(a,b)+(x,y)=(a+x,(b<<k/2)+y)$
从含义上就能解释,只不过是把劈成两半的二进制数合并。
然后就一模一样的check就可以了。

1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 using namespace std; 5 int read(){ 6 register int p=0;register char ch=getchar(); 7 while(ch<'0'||ch>'9')ch=getchar(); 8 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 9 return p; 10 } 11 int a[500005],n,k,p,k1,k2,rt;long long ctb[31][2],w[15000005]; 12 int c[15000005][2],cnt; 13 struct Pair{ 14 long long w;int id; 15 friend bool operator<(Pair a,Pair b){ 16 return a.w<b.w||(a.w==b.w&&a.id<b.id); 17 } 18 }p1[32769],p2[32769]; 19 void insert(int &p,int v,int al){ 20 if(!p)p=++cnt;w[p]++; 21 if(al==-1)return; 22 insert(c[p][v&1<<al?1:0],v,al-1); 23 ctb[al][v&1<<al?1:0]+=w[c[p][v&1<<al?0:1]]; 24 } 25 int chk(Pair x){ 26 long long lower=0; 27 for(int i=0;i<1<<k1;++i)lower+=upper_bound(p2,p2+(1<<k2),(Pair){x.w-p1[i].w,x.id-(p1[i].id<<k2)})-p2;cerr<<lower<<endl; 28 return lower; 29 } 30 int main(){//freopen("f3.in","r",stdin); 31 n=read(),k=read(),p=read();k1=k>>1;k2=k-k1; 32 for(int i=1;i<=n;++i)insert(rt,read(),k-1); 33 for(int i=0;i<1<<k1;++i)for(int j=0;j<k1;++j)p1[i].w+=ctb[j+k2][i&1<<j?1:0]; 34 for(int i=0;i<1<<k1;++i)p1[i].id=i; 35 for(int i=0;i<1<<k2;++i)for(int j=0;j<k2;++j)p2[i].w+=ctb[j][i&1<<j?1:0]; 36 for(int i=0;i<1<<k2;++i)p2[i].id=i; 37 sort(p1,p1+(1<<k1));sort(p2,p2+(1<<k2)); 38 long long l=0,r=1ll*n*(n-1)>>1,ans,ans2; 39 while(l<=r)if(chk((Pair){l+r>>1,0})<p)l=(ans=l+r>>1)+1;else r=(l+r>>1)-1; 40 l=0,r=(1<<k)-1; 41 while(l<=r)if(chk((Pair){ans,l+r>>1})<p)l=(l+r>>1)+1;else r=(ans2=l+r>>1)-1; 42 printf("%lld %lld ",ans,k?ans2:0); 43 }
然而第一个子任务挂了,不知道为什么。我判掉了。。。怪没脸的。。。