CJ 8.31 test
Posted cjoiershiina-mashiro
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CJ 8.31 test相关的知识,希望对你有一定的参考价值。
A
j打成i然后100->0.
我们先考虑把第一个操作并到第二个操作里面。
显然把某二进制位取反等于异或上\(2^i\)。
去个重应该可以降一点复杂度。最多38个元素。
我们现在把要做的变成了求使用一个数组中的元素\(\a_1,\cdots,a_n\\)相互异或成\(p=s\oplus t\)的最短步数。
我们可以用bfs来做一个类似最短路的问题,对于每一个询问,我们可以在计算出当前答案之后停止计算,等之后的询问需要计算时继续计算。
并且\(dis\)数组一开始可以设置为只异或上\(2^i\)变成\(0\)的最短步数,可以降一点复杂度。
实际上并不需要这些剪枝。
#include<bits/stdc++.h>
using namespace std;
const int P=998244353,N=262144;
int dis[N],d[N],a[39];
queue<int>q;
int read()int x=0;char c=getchar();while(c<48||c>57)c=getchar();while(c>=48&&c<=57)x=x*10+c-48,c=getchar();return x;
int inc(int a,int b)a+=b;return a>=P? a-P:a;
int mul(int a,int b)return 1ll*a*b%P;
int main()
freopen("A.in","r",stdin),freopen("A.out","w",stdout);
int T,n,m,Q,i,j,p,x,ans=0;
for(i=1;i<N;++i) d[i]=d[i^(i&-i)]+1;
for(T=read();T;--T)
m=read(),Q=read(),n=ans=0,memcpy(dis,d,sizeof dis);
while(!q.empty()) q.pop();
for(x=1;x<N;x<<=1) dis[x]=1,q.push(x),a[++n]=x;
for(i=1;i<=m;++i)x=read();if(dis[x]^1)dis[x]=1,q.push(x),a[++n]=x;
for(i=1;i<=Q;++i)
p=read()^read();
while(!q.empty())
x=q.front(); if(dis[x]>=dis[p]) break; q.pop();
for(j=1;j<=n;++j) if(dis[x^a[j]]>dis[x]+1) dis[x^a[j]]=dis[x]+1,q.push(x^a[j]);
ans=inc(ans,mul(i,dis[p]));
printf("%d\n",ans);
C
我们可以把答案分成两部分。
第一部分是竖的和横的,这部分的答案是\(n\choose3m+m\choose3n\),稍微特判一下。
第二部分是斜着的。
我们只需要考虑从左下到右上的(原点在最左下),从左上到右下的和从左下到右上的是一样的。
然后我们可以把其中一个端点平移至原点\((0,0)\)。
所以实际上就是枚举一个点\((i,j)\),计算在这个点到原点的线段上有多上个点,即多少对三点共线,这个答案应该是\(gcd(i,j)-1\)。(注意这里是线段,这样才能保证不重复算)
并且这样的还可以进行平移,答案需要乘上\((n-i)(m-j)\)。
以上是关于CJ 8.31 test的主要内容,如果未能解决你的问题,请参考以下文章