省选摸你31
很没有自信,对待ZR的题,已经出现了阴影......
仍旧以为T1是结论题,边睡觉边等待灵光一现,结果两个小时过去了啥也没想出来,但是大题方向是对的,没找到排名和权值在线性基上的对应关系,额好像是知识漏洞
T2一开始看错题了,以为直接拿到了70分,然后知道了之后心态有点爆炸,一直认为如果得到了一个序列的\(\mathcal{O(n)}\)做法,那么这个题就可以切了,所以思路就一直在这上面,并且之前做过一道类似的是枚举中间的点的位置的,就一直没有转过弯来,后来考后经过zxb的指点,得到了预处理\(\mathcal{O(n^2)}\)的做法,启发,可以更换枚举方式,有奇效!!
T3是一点都不会,只有暴力分,剩下的甚至一点头绪都没有,树的做法是点分治,然而我并没有想到,以后注意树上的距离问题是可以很好的借助点分治来解决的,这里就利用了点分治来前缀优化建图,说实话之前用过一次了,我给忘记了...
T1 神必的集合
可以想得到是线性基,异或这东西,按位和线性基选一个就好了
考场上的思路不在找排名和权值的关系上,而在怎么统计方案数上,哎,以后做题不要着急的做题,而要先观察性质...
如果我们要求一个线性基上能得到的所有数中某一个数的权值,就把线性基有数的位置,注意是位置,对应在当前数的值提出来
组成一个二进制数,这个二进制数就是这个数的排名,证明的话,我们把线性基消成对角线型的,发现对于这个数来说要异或的就是对应的位置为1的位置上的数,额,不太好描述,算了算了自己baidu吧
而我们这个题要找的正是排名和权值的关系!!
我们现在有一堆排名和权值,我们将他们插入到线性基上,有一个结论,设\(rk_i\)表示排名为i的数,则有\(rk_i \text{^} rk_j=rk_{i \text{^}j}\)
这个根据排名得出的原因显然好吧...
所以我们在建立线性基的时候顺便把每一个位置的排名也搞出来,这样我们就得到了不超过n个排名和权值对应的关系
我们称线性基上有值的位为主元,此时我们的线性基上已经有几个位置是有值的了,我们称之为钦定的主元
那么我们当前的线性基一定是可行线性基的一个子线性基,这个定义挺模糊的,就是这个线性基再插入几个数可以得到可行线性基
而我们的钦定主元是由输入的数决定的,所以我们的钦定主元是不可变的,而非钦定主元后面的非主元是可以任意的,只要最后把其他主元消一下就好了,没有什么太大的关系
这样我们有了一个\(\mathcal{O(2^n*n)}\)的做法
直接枚举谁是主元,并且判断是否合法,必须含有钦定主元并且排名和权值能对上,那么这时的方案数就是2的所有非钦定主元的非主元个数次方,加起来就是答案
那么我们考虑这个过程,就是确定主元是谁的过程,如果是非钦定主元那就乘个系数
所以我们dp就行了,转移的时候看是不是非钦定主元,并且看排名和权值的这一位是否相同即可
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=205;
const int mod=998244353;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int n,mm,m,ans;
struct IFM{int x,y;}ifm[N];
int ji[N],cj;bool vis[N];
int mai[65],ba[65],rk[65];
int dp[65][65];
signed main(){
freopen("set.in","r",stdin);
freopen("set.out","w",stdout);
n=read();mm=read();
fo(i,1,mm){
int x=read()-1,y=read();
fu(j,n-1,0)if((y>>j)&1){
if(ba[j])y^=ba[j],x^=rk[j];
else {
ifm[++m].x=rk[j]=x;
ifm[m].y=ba[j]=y;
break;
}
}
if(y==0&&x!=0){printf("0");return 0;}
}int mx=0;
fo(i,1,m){
fu(j,n-1,0)if((ifm[i].y>>j)&1){vis[j]=true;break;}
int now=0;fu(j,n-1,0)if((ifm[i].x>>j)&1){now=j;break;}
mx=max(mx,now+1);
}
bool fl=true;
fo(k,1,m)if((ifm[k].x&1)!=(ifm[k].y&1))fl=false;
if(fl){
if(vis[0])dp[0][1]=1;
else dp[0][1]=dp[0][0]=1;
}
else if(!vis[0])dp[0][0]=1;
fo(i,0,n-2)fo(j,0,i+1){
if(!dp[i][j])continue;
fl=true;
fo(k,1,m)if(((ifm[k].x>>j)&1)!=((ifm[k].y>>i+1)&1))fl=false;
if(fl){
if(vis[i+1])dp[i+1][j+1]=(dp[i+1][j+1]+dp[i][j])%mod;
else {
dp[i+1][j+1]=(dp[i+1][j+1]+dp[i][j]*ksm(2,i+1-j))%mod;
dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod;
}
}
else if(!vis[i+1])dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod;
}
fo(i,mx,n)ans=(ans+dp[n-1][i])%mod;
printf("%lld",ans);
return 0;
}
2^n
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=205;
const int mod=998244353;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int n,mm,m,ans;
struct IFM{int x,y;}ifm[N];
bool com(IFM a,IFM b){return a.x<b.x;}
int ji[N],cj;bool vis[N];
int mai[65],ba[65],rk[65];
signed main(){
freopen("set.in","r",stdin);
freopen("set.out","w",stdout);
n=read();mm=read();
fo(i,1,mm){
int x=read()-1,y=read();
fu(i,n-1,0)if((y>>i)&1){
if(ba[i])y^=ba[i],x^=rk[i];
else {
ifm[++m].y=ba[i]=y;
ifm[m].x=rk[i]=x;
break;
}
}
}
// cerr<<m<<endl;
int u=(1<<n)-1,bas=0;
// cerr<<u<<endl;
fo(i,1,m){
// cerr<<ifm[i].x<<" "<<ifm[i].y<<endl;
fu(j,n-1,0)if((ifm[i].y>>j)&1){
bas|=(1<<j);vis[j]=true;break;
}
}
// cerr<<bas<<endl;
for(int ss=u^bas,t=u^bas;ss>=0;ss--){
if(ss<0)break;ss&=t;
int s=u^ss;memset(mai,0,sizeof(mai));
fu(i,n-1,0)if((s>>i)&1)mai[i]=1;//cerr<<i<<" ";cerr<<endl;
bool fl=true;
fo(i,1,m){
int now=0;
fu(j,n-1,0)if(mai[j])now=(now<<1)+((ifm[i].y>>j)&1);//cerr<<j<<" ";
// cerr<<s<<" "<<ifm[i].y<<" "<<ifm[i].x<<" "<<now<<endl;
if(now!=ifm[i].x){fl=false;break;}
}
if(!fl)continue;
fo(i,1,n-1)mai[i]+=mai[i-1];//cerr<<mai[i]<<" ";cerr<<endl;
// cerr<<s<<endl;
int res=1;
fo(i,1,n-1)if(mai[i]!=mai[i-1]&&!vis[i]){
res=res*ksm(2,i-mai[i-1])%mod;
}
ans=(ans+res)%mod;
}
printf("%lld",ans);
return 0;
}
T2 法阵
考场上一直想着枚举中间的数,用数据结构枚举两边的数
没想到正解是枚举前两个数,并且前两个数的选择种类是\(\mathcal{O(n)}\)的,因为前两个数中间不能有比这两个数大的数
如果有的话,那么好,这个数比你选的这两个优秀!!
于是我们用线段树把答案维护在最后一个数上,到时候直接查就行了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=5e5+5;
int n,a[N],T;
struct XDS{
#define ls x<<1
#define rs x<<1|1
int mx[N*4],ms[N*4],tg[N*4];
void pushup(int x){
mx[x]=max(mx[ls],mx[rs]);
ms[x]=max(ms[ls],ms[rs]);
return ;
}
void pushdown(int x){
ms[ls]=max(ms[ls],mx[ls]+tg[x]);
tg[ls]=max(tg[ls],tg[x]);
ms[rs]=max(ms[rs],mx[rs]+tg[x]);
tg[rs]=max(tg[rs],tg[x]);
tg[x]=0;return ;
}
void build(int x,int l,int r){
if(l==r)return mx[x]=ms[x]=a[l],void();
int mid=l+r>>1;
build(ls,l,mid);build(rs,mid+1,r);
pushup(x);return ;
}
void ins(int x,int l,int r,int ql,int qr,int v){
if(ql>qr)return ;
if(ql<=l&&r<=qr){
ms[x]=max(ms[x],mx[x]+v);
tg[x]=max(tg[x],v);return ;
}
int mid=l+r>>1;if(tg[x])pushdown(x);
if(ql<=mid)ins(ls,l,mid,ql,qr,v);
if(qr>mid)ins(rs,mid+1,r,ql,qr,v);
pushup(x);return ;
}
int query(int x,int l,int r,int ql,int qr){
if(ql>qr)return 0;
if(ql<=l&&r<=qr)return ms[x];
int mid=l+r>>1,ret=0;if(tg[x])pushdown(x);
if(ql<=mid)ret=max(ret,query(ls,l,mid,ql,qr));
if(qr>mid)ret=max(ret,query(rs,mid+1,r,ql,qr));
pushup(x);return ret;
}
#undef ls
#undef rs
}xds;
int sta[N],top,ans[N];
vector<int> vec[N];
struct Q{int x,y,id;}q[N];
bool com(Q a,Q b){return a.x<b.x;}
signed main(){
freopen("fz.in","r",stdin);
freopen("fz.out","w",stdout);
n=read();
fo(i,1,n){
a[i]=read();
while(top&&a[sta[top]]<=a[i]){vec[sta[top]].push_back(i);top--;}
if(top)vec[sta[top]].push_back(i);sta[++top]=i;
}
xds.build(1,1,n);
T=read();
fo(i,1,T)q[i].x=read(),q[i].y=read(),q[i].id=i;
sort(q+1,q+T+1,com);q[T+1].x=n+1;
fu(i,T,1){
if(q[i].x<q[i+1].x){
fu(j,q[i+1].x-1,q[i].x){
for(int k:vec[j]){
// cerr<<j<<" "<<k<<" "<<k-j+k<<endl;
xds.ins(1,1,n,k-j+k,n,a[j]+a[k]);
}
}
}
ans[q[i].id]=xds.query(1,1,n,q[i].x,q[i].y);
}
fo(i,1,T)printf("%lld\n",ans[i]);
}
T3 旅行
我们用点分治优化建图,树上距离可以用点分治
具体的操作办法就是,对于一个分治中心,建出深度个虚点,深度大的向深度小的连边,对应深度向对应点连边
然后点向对应的可以达到的深度的点连边,注意是跨过分治中心的深度
注意这样建图的条件是,自己和自己建立更长的边对答案没有影响
于是我们发现还有50条非树边,于是如果经过非树边那就一定经过这个非树边的某一个节点,我们把这样的50个节点看成点分治的分治中心做就好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=2e5+55;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,d[N],c[N];
struct E{int to,nxt;}e[N*2];
int head[N],rp=1,vie[N*2];
void add_edg(int x,int y){e[++rp].to=y;e[rp].nxt=head[x];head[x]=rp;}
struct DIJ{
struct E{int to,nxt,val;}e[N*200];
int head[N*200],rp,seg;
void add_edg(int x,int y,ll z){
e[++rp].to=y;e[rp].nxt=head[x];
e[rp].val=z;head[x]=rp;
}
struct node{
int x;ll ds;node(){}node(int a,ll b){x=a;ds=b;}
bool operator < (node a)const{return ds>a.ds;}
};
priority_queue<node> q;
ll dis[N*200];
bool vis[N*200];
void dij(){
memset(dis,0x3f,sizeof(dis));
q.push(node(1,0));dis[1]=0;
while(!q.empty()){
int x=q.top().x;q.pop();
if(vis[x])continue;vis[x]=true;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(dis[y]<=dis[x]+e[i].val)continue;
dis[y]=dis[x]+e[i].val;
q.push(node(y,dis[y]));
}
}
fo(i,2,n)printf("%lld\n",dis[i]);
}
}dij;
int rt,mx,ms[N],siz[N],bia[N];
bool vis[N];
void findrt(int x,int f,int sz){
siz[x]=1;ms[x]=0;
for(int i=head[x];i;i=e[i].nxt){
if(vie[i])continue;
int y=e[i].to;
if(y==f||vis[y])continue;
findrt(y,x,sz);
siz[x]+=siz[y];
ms[x]=max(ms[x],siz[y]);
}
ms[x]=max(ms[x],sz-siz[x]);
if(ms[x]<mx)rt=x,mx=ms[x];
}
int dep[N],mxd,ji[N],cj,vib[N];
void bfs(int x,int tp){
cj=mxd=0;
queue<int> q;while(!q.empty())q.pop();
q.push(x);dep[x]=0;vib[x]=true;
while(!q.empty()){
int x=q.front();q.pop();
ji[++cj]=x;mxd=dep[x];
for(int i=head[x];i;i=e[i].nxt){
if(vie[i]&&tp)continue;
int y=e[i].to;
if(vis[y]||vib[y])continue;
dep[y]=dep[x]+1;
q.push(y);vib[y]=true;
}
}
fo(i,1,cj)vib[ji[i]]=false;
}
void sol(int x,int tp=1){
bfs(x,tp);bia[0]=x;
fo(i,1,mxd){
bia[i]=++dij.seg;
dij.add_edg(bia[i],bia[i-1],0);
}
fo(i,1,cj){
dij.add_edg(bia[dep[ji[i]]],ji[i],0);
if(d[ji[i]]>=dep[ji[i]])dij.add_edg(ji[i],bia[min(mxd,d[ji[i]]-dep[ji[i]])],c[ji[i]]);
}
}
void dive(int x,int sz){
sol(x);vis[x]=true;
for(int i=head[x];i;i=e[i].nxt){
if(vie[i])continue;
int y=e[i].to;
if(vis[y])continue;mx=inf;
if(siz[y]>siz[x])findrt(y,x,sz-siz[x]),dive(rt,sz-siz[x]);
else findrt(y,x,siz[y]),dive(rt,siz[y]);
}
}
int xh[N];
void dfs(int x,int f){
xh[x]=xh[f]+1;
for(int i=head[x];i;i=e[i].nxt){
if(vie[i])continue;
int y=e[i].to;
if(y==f)continue;
if(xh[y]&&xh[y]<xh[x]){vie[i]=vie[i^1]=true;continue;}
dfs(y,x);
}
}
signed main(){
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
n=dij.seg=read();m=read();
fo(i,1,m){
int x=read(),y=read();
add_edg(x,y);add_edg(y,x);
}
dfs(1,0);
fo(i,1,n)d[i]=read(),c[i]=read();
mx=inf;findrt(1,0,n);dive(rt,n);
cerr<<dij.seg<<" "<<dij.rp<<endl;
memset(vis,false,sizeof(vis));
cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
fo(i,1,rp)if((!(i&1))&&vie[i])sol(e[i].to,0);
cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
dij.dij();cerr<<dij.seg<<" "<<dij.rp<<endl;
}