洛谷3月月赛II T2-4简要题解

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ https://blog.csdn.net/corsica6/article/details/88780746

T2 无论怎样神树大人都会删库跑路

注意暴力双端队列+桶计算时,一轮中可能对某个长为 1 0 5 10^5 的小串反复插入,会T。

判断字符串相同的条件比较特殊:每个数字出现次数相同则字符串相同。桶可以转成哈希—— b a s e base 进制的每一位系数分别表示这个位数的出现次数。
记录每个小串的整体哈希值,插入删除就是 O ( 1 ) O(1) 的。

首先暴力前几轮(一轮指的是完整的 m m 次操作),使得小串连接总长 T \geq T ,后面每轮都是等价的,再多进行一轮算一下每次操作的贡献即可。

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define sc second
#define gc getchar
#define pb push_back
using namespace std;
const int N=1e5+10,p1=1e9+7,p2=1e9+9,bs=1e5+7;
typedef long long ll;
typedef double db;

int n,T,q,m,R[N],sz[N],a[N];
int r[N],gx[N],rnd,L,t[N];
pii pw[N],ori,v,nw;ll ans,whl;
vector<pii>hz[N];

char cp;
inline int rd()
{
	cp=gc();int x=0,f=1;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=-1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	return f*x;
}

inline int ad(int x,int y,int p){x+=y;return x>=p?x-p:x;}
inline int dc(int x,int y,int p){x-=y;return x<0?x+p:x;}
inline pii operator *(pii a,int b){return mkp((ll)a.fi*b%p1,(ll)a.sc*b%p2);}
inline pii operator +(pii a,int b){return mkp(ad(a.fi,b,p1),ad(a.sc,b,p2));}
inline pii operator +(pii a,pii b){return mkp(ad(a.fi,b.fi,p1),ad(a.sc,b.sc,p2));}
inline pii operator -(pii a,pii b){return mkp(dc(a.fi,b.fi,p1),dc(a.sc,b.sc,p2));}
inline pii operator *(pii a,pii b){return mkp((ll)a.fi*b.fi%p1,(ll)a.sc*b.sc%p2);}

struct P{int id,len;}tp;
deque<P>que;

inline void upd(int x)
{
    nw=nw+hz[x][sz[x]];L+=sz[x];
	que.push_back((P){x,sz[x]});
    for(;L>T;){
		tp=que.front();que.pop_front();nw=nw-hz[tp.id][tp.len];
		if(L-tp.len>=T) L-=tp.len;else{
			nw=nw+hz[tp.id][T-L+tp.len];
			que.push_front((P){tp.id,T-L+tp.len});L=T;
		}
	}
}

int main(){
	int i,j,x,y,mx=0;pw[0]=mkp(1,1);
	for(i=1;i<N;++i) pw[i]=pw[i-1]*bs;
    n=rd();T=rd();q=rd();
	for(i=1;i<=T;++i) ori=ori+pw[rd()+1];
    for(i=1;i<=n;++i){
       sz[i]=x=rd();hz[i].resize(x+1);v.fi=v.sc=0;
	   for(j=1;j<=x;++j) {a[j]=rd()+1;v=v+pw[a[j]];}
	   for(j=x;j>0;--j) {hz[i][j]=v;v=v-pw[a[x-j+1]];}
	}
	for(m=rd(),i=1;i<=m;++i) whl+=sz[(r[i]=rd())];
	rnd=((T-1)/whl+1)*m;
	for(i=1;i<=q && i<=rnd;++i){upd(r[(i-1)%m+1]);ans+=(L==T && ori==nw);}
	for(i=1;i<=m && rnd+i<=q;++i){upd(r[(i-1)%m+1]);gx[i]=gx[i-1]+(ori==nw);}
	if(q>rnd) ans+=(ll)gx[m]*((q-rnd)/m)+gx[q%m];
    printf("%lld",ans);
	return 0;
}



T3 OwenOwl 不学车也不删库

神仙构造题还是不会做 (*  ̄︿ ̄)

官方题解:

数形结合,不妨将原图看做一维直线上的模 p p 意义下的 p p 个点 ( 0 , 1 , . . . , p 1 ) (0,1,...,p-1) p k p^k 的拓展相当于拓展到了模 p p 意义下的 x x 维空间中(一共 p x p^x )个点,让原图中的每个点对应空间中的一个点

枚举一个非零向量 v v 和起点 x x x , x + v , . . . , x + ( p 1 ) v x,x+v,...,x+(p-1)v 分为一组即可。
两个不同点唯一确定剩下的 p 2 p-2 个点,且由于 p p 质数,一定有解。

std

#include<bits/stdc++.h>
#define pb push_back
#define gc getchar
using namespace std;
const int N=2010;
typedef long long ll;
typedef double db;

int n=1,p,k,dlt,q[N];
bool g[N][N];

inline int cal(int x,int y)
{
    int re=0,i,j,pw=1;
    for(i=0;i<k;++i,pw*=p,x/=p,y/=p)
     re+=(p+x%p+y%p)%p*pw;
    return re;
}

int main(){
    int i,j,x,y; 
    scanf("%d%d",&p,&k);puts("YES");
    for(i=1;i<=k;++i) n*=p;
    for(i=0;i<n;++i)
     for(j=i+1;j<n;++j) if(!g[i][j]){
     	dlt=cal(j,-i);q[0]=i;
     	for(x=1;x<p;++x) q[x]=cal(q[x-1],dlt);
     	for(x=0;x<p;++x){
     		printf("%d ",q[x]);
     		for(y=0;y<p;++y)
     		 g[q[x]][q[y]]=true;
        }
        putchar('\n');
     }
    return 0;
}

T4 总而言之神J要去练习篮球

在这里插入图片描述

万古神犇mcfx,扑通扑通跪下来。
这题好神…膜了一发官方题解

为方便处理,每个询问 [ l x , l y , r x , r y ] [lx,ly,rx,ry] 转化为四个左上角为 ( 0 , 0 ) (0,0) 的矩阵的贡献和 ( [ 0 , 0 , r x , r y , 1 ] , [ 0 , 0 , r x , l y 1 , 1 ] , [ 0 , 0 , l x 1 , r y , 1 ] , [ 0 , 0 , l x 1 , l y 1 , 1 ] ) ([0,0,rx,ry,1],[0,0,rx,ly-1,-1],[0,0,lx-1,ry,-1],[0,0,lx-1,ly-1,1])

首先考虑 W = H = 1 W=H=1 的情况:
问题转换为了,对于 f [ X ] = x = 0 r x y = 0 r y [ x   x o r   y = X ] f[X]=\sum\limits_{x=0}^{rx}\sum\limits_{y=0}^{ry}[x \ xor \ y=X] ,求 f [ X ] f[X] 有多少种取值,以及每种值的出现次数。

对于每个特定的 X X ,数位 D P DP 枚举 X X l   x o r   r l\ xor \ r 第一次分开的位求解 f ( X ) f(X)

int cal(int r1,int r2,int x){
    int R=r1^r2;
    int ans=0;
    for(int i=30;i>=0;--i){
        if(((r1>>i&1)||(r2>>i&1))){
            int a=r1>>i&1,b=r2>>i&1,c=x>>i&1;
            int d=(1<<i);
            if(a&&(b==c))ans+=r2%d+1;
            if(b&&(a==c))ans+=r1%d+1;
            if(a&&b&&0==c)ans+=d;
        } 
        if((x>>i&1)!=(R>>i&1))break;
    }
    return ans+(x==R);
}

发现不同的 X X 只是 b r e a k break 的位不同(即和 l   x o r   r l\ xor \ r 第一个不同的位不同),所以问题总复杂度也是 O ( log ) O(\log ) 的。

再考虑 W , H 1 W,H\neq 1 的情况,怎么把它转化成 W = H = 1 W=H=1 的情况呢?

考虑行列差分,此时左上角分别为 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) 的矩阵相同的条件:

  • x 1   x o r   ( x 1 + 1 ) , . . . , ( x 1 + w 2 )   x o r   ( x 1 + w 1 )   =   x 2   x o r   ( x 2 + 1 ) , . . . , ( x 2 + w 2 )   x o r   ( x 2 + w 1 ) x_1 \ xor \ (x_1+1),...,(x_1+w-2)\ xor \ (x_1+w-1) \ = \ x_2 \ xor \ (x_2+1),...,(x_2+w-2)\ xor \ (x_2+w-1) y 1 , y 2 y_1,y_2 同理
  • a [ x 1 ] [ y 1 ] = a [ x 2 ] [ y 2 ] a[x_1][y_1]=a[x_2][y_2]

由于 x   x o r   ( x + 1 ) = 2 l o w b i t ( x + 1 ) 1 x\ xor \ (x+1)=2^{lowbit(x+1)}-1 ,所以实际上就是判断 l o w b i t ( x 1 + 1 ) , . . . , l o w b i t ( x 1 + w 1 ) = l o w b i t ( x 2 + 1 ) , . . . , l o w b i t ( x 2 + w 1 ) lowbit(x_1+1),...,lowbit(x_1+w-1)=lowbit(x_2+1),...,lowbit(x_2+w-1)

发现在 x x 不断 + 1 +1 的过程中,会影响到的二进制位有 0 max ( l o w b i t ( x + i ) ) ( 1 i &lt; w ) 0-\max(lowbit(x+i))(1\leq i&lt;w) ,等价于 x   x o r   ( x + w 1 ) x\ xor \ (x+w-1) 的最高位,设其为 k k
x 1 , x 2 x_1,x_2 序列相等,则必须满足后 k k 位相同,相当于是一个 2 k 2^k 的循环节。
y 1 , y 2 y_1,y_2 同理。

枚举每个行循环节 2 a 2^a ,列循环节 2 b 2^b 所对应的行列
那么问题就变成了求解 f [ X ] = x × 2 a r x y × 2 b r y [ ( x × 2 a )   x o r   ( y × 2 b ) = X ] f[X]=\sum\limits_{x\times 2^a\leq rx}\sum\limits_{y\times 2^b \leq ry}[(x\times 2^a)\ xor \ (y\times 2^b)=X] 的值的种类数和对应出现次数。

进一步还可以得到结论:
枚举到 2 a , 2 b 2^a,2^b 时,可以强制行列循环节均为 max ( 2 a , 2 b ) \max(2^a,2^b)
因为一侧不同,另一侧改变时,异或值一定不同,所以可以分开统计。

问题相当于枚举行列末 k k 位相同,再套上 W = H = 1 W=H=1 的情况计算。

复杂度 log 2 \log ^2

对着std抄了一遍

#include<bits/stdc++.h>
typedef long long ll;
#define pii pair<ll,ll>
#define mkp make_pair
#define fi first
#define sc second
#define gc getchar
#define pb push_back
using namespace std;
const int mod=1e9+7;
typedef double db;

int tk,W,H,K,ans;
vector<pii>a;

inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline int fp(int x,int y)
{
	int re=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	 if(y&1) re=(ll)re*x%mod;
	return re;
}

/*single point*/
inline void mk(vector<pii>&v,int l,int r,ll vl)
{if(vl){v.pb(mkp(l,vl));v.pb(mkp(r,-vl));}}

//模拟数位DP 枚举所有数 
void mat(int x,int y,vector<pii>&v,int pr)
{
	if(x<0 || y<0) return;
	int a,b,c,d,i,R=(x^y),res=0;
	ll ss=0,rss;
	for(i=30;i>=0;--i){

		//枚举第一次分离的位 
		rss=ss;d=1<<i;a=x&d;b=y&d;c=(R&d)^d;
		if(a&&(b==c)) rss+=(y%d)+1;
		if(b&&(a==c)) rss+=(x%d)+1;
		if(a&&b&&(c==0)) rss+=d;
		
		res|=(R&d);
		mk(v,res^d,(res^d)+d,pr*rss);c^=d;
		if(a&&(b==c)) ss+=(y%d)+1;
		if(b&&(a==c)) ss+=(x%d)+1;
		if(a&&b&&(c==0)) ss+=d;
	}
	mk(v,res,res+1,pr*(ss+1));
}

void Single_sol(int lx,int rx,int ly,int ry,ll cnt)
{
	if(lx>rx || ly>ry) return;
	vector<pii>re;int i,sz;ll ss=0;
    mat(rx,ry,re,1);mat(lx-1,ry,re,-1);mat(rx,ly-1,re,-1);mat(lx-1,ly-1,re,1);
	sort(re.begin(),re.end());sz=re.size();
	for(i=0;i<sz;++i){
		ss+=re[i].sc;
		if(ss && i+1<sz && re[i+1].fi>re[i].fi) a.pb(mkp(ss,cnt*(re[i+1].fi-re[i].fi)%mod));
	}
}

/* step by step (2^k)*/
struct P{ll d;int l,r;};

inline void ins(int l,int r,int ql,int qr,vector<P>&v)
{if(l<=r && ql<=qr) v.pb((P){r-l+1,ql,qr});}

inline void nd(int lx,int rx,int liml,int limr,int stp,vector<P>&v)
{
    int L=max(lx%stp,liml),R=min(rx%stp,limr),l=lx/stp,r=rx/stp;
    if(L<=R){
		ins(liml,L-1,l+1,r,v);
        ins(L,R,l,r,v);
		ins(R+1,limr,l,r-1,v);
	}else{
		ins(liml,R,l+1,r,v);
		ins(max(liml,R+1),min(limr,L-1),l+1,r-1,v);
		ins(L,limr,l,r-1,v);
	}
}

//x*2^k xor y*2^k = X
inline void Step_sol(int lx,int rx,int ly,int ry,int llx,int lrx,int lly,int lry,int k)
{
    if(llx>lrx || lly>lry) return;
	vector<P>ra,rb;
	nd(lx,rx,llx,lrx,1<<k,ra);nd(ly,ry,lly,lry,1<<k,rb);
	for(P a:ra)
	 for(P b:rb)
	  Single_sol(a.l,a.r,b.l,b.r,a.d*b.d%mod);
}

/*major*/
void sol(int lx,int rx,int ly,int ry)
{
     if(W==1 && H==1) {Single_sol(lx,rx,ly,ry,1);return;}
     rx=rx-W+1;ry=ry-H+1;int c=0,L=1;
	 for(;L<W || L<H;L<<=1) c++;//max( 0^(W-1) | 0^(H-1) ) 共c位 循环节 2^c
	 Step_sol(lx,rx,ly,ry,0,L-W,0,L-H,c);
	 for(c++;c<=30;L<<=1,c++){
         Step_sol(lx,rx,ly,ry,L-W+1,L-1,0,(L<<1)-H,c);// 0->(L<<1)-H 所有xor <2^(c+1)的数 
		 //L-W+1 -> L-1 注意只能算一次 
		 Step_sol(lx,rx,ly,ry,0,L-W,L-H+1,L-1,c);
		 Step_sol(lx,rx,ly,ry,L,(L<<1)-W,L-H+1,L-1,c);
	 }
}

int main(){
	int i,j,sz,lx,ly,rx,ry;
    for(scanf("%d",&tk);tk;--tk){
		a.clear();ans=0;
		scanf("%d%d%d%d%d%d%d",&lx,&rx,&ly,&ry,&W,&H,&K);
        sol(lx,rx,ly,ry);
		sort(a.begin(),a.end());sz=a.size();
		for(i=0;i<sz;++i){
           if((!i)||(a[i].fi!=a[i-1].fi)) j=fp(a[i].fi%mod,K);
		   ad(ans,(ll)j*a[i].sc%mod);
		}
		printf("%d\n",(ll)ans*fp((ll)(rx-lx-W+2)*(ry-ly-H+2)%mod,mod-1-K)%mod);//注意是-k次幂(k^(mod-2)) 不是mod-2+k 
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/88780746
今日推荐