zoukankan      html  css  js  c++  java
  • 「GXOI / GZOI2019」简要题解

    「GXOI / GZOI2019」简要题解

    LOJ#3083. 「GXOI / GZOI2019」与或和

    https://loj.ac/problem/3083

    题意:求一个矩阵的所有子矩阵的与和 和 或和。

    分析:

    • 或和与是一个东西,只要把所有数都异或上((1<<31)-1)然后再从总答案中减掉就能互相转化,考虑求与。
    • 枚举每一位,转化成算有多少个全(1)子矩形,单调栈经典问题。总时间复杂度(mathrm{O}(n^2log n))

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    using namespace std;
    typedef long long ll;
    #define N 1050
    #define mod 1000000007
    int mask=(1ll<<31)-1,n,a[N][N];
    int b[N][N],up[N],S[N],tp;
    ll work() {
    	int i,j,k;
    	ll re=0;
    	for(k=0;k<=30;k++) {
    		for(i=1;i<=n;i++) up[i]=0;
    		for(i=1;i<=n;i++) {
    			ll now=0;
    			tp=0;
    			S[0]=0;
    			for(j=1;j<=n;j++) {
    				b[i][j]=a[i][j]&(1<<k);
    				if(!b[i][j]) up[j]=0;
    				else up[j]++;
    				if(!b[i][j]) tp=0,S[0]=j,now=0;
    				else {
    					while(tp&&up[S[tp]]>=up[j]) now-=ll(up[S[tp]])*(S[tp]-S[tp-1]),tp--;
    					S[++tp]=j; now+=ll(up[j])*(S[tp]-S[tp-1]);
    					now%=mod; re=(re+now*(1<<k))%mod;
    				}
    			}
    		}
    	}
    	return re%mod;
    }
    int main() {
    	scanf("%d",&n);
    	ll tot=0;
    	int i,j;
    	for(i=1;i<=n;i++) for(j=1;j<=n;j++) {
    		scanf("%d",&a[i][j]);
    		tot=(tot+i*j);
    	}
    	tot%=mod;
    	ll ans1=work();
    	for(i=1;i<=n;i++)for(j=1;j<=n;j++)a[i][j]^=mask;
    	ll ans2=work();
    	ans2=(tot*mask%mod-ans2)%mod;
    	printf("%lld %lld
    ",(ans1+mod)%mod,(ans2+mod)%mod);
    }
    

    LOJ#3084. 「GXOI / GZOI2019」宝牌一大堆

    https://loj.ac/problem/3084

    分析:

    • 对于七对子和国土无双,可以拿出来(mathrm{O}(13^2+35log 35)) 处理。
    • 剩下的情况,可以看成是(1)组雀头和(4)组杠子或面子。
    • 直接设(f[i][j][k][l][x][y])表示前(i)张牌,(i-2,i-1,i)分别作为顺子的最后一张拿走了(j,k,l)张牌,总共的杠子+面子数为(x),雀头数为(y)
    • 然后(l)这维不用存并且(i)可以滚动掉,同时可以发现这个是跑不满的。
    • 注意转移的时候不要超过(1)(4)的限制。
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    #define N 55
    ll mi[N],C[N][N];
    int a[N],b[N];
    inline void upd(ll &x,ll y) {x=x>y?x:y;}
    int trans(char *w) {
    	if(strlen(w+1)==2) {
    		int i;
    		if(w[2]=='m') i=1;
    		else if(w[2]=='p') i=2;
    		else i=3;
    		return (i-1)*9+w[1]-'0';
    	}else {
    		if(w[1]=='E') return 28;
    		if(w[1]=='S') return 29;
    		if(w[1]=='W') return 30;
    		if(w[1]=='N') return 31;
    		if(w[1]=='Z') return 32;
    		if(w[1]=='B') return 33;
    		if(w[1]=='F') return 34;
    	}
    	return -1;
    }
    ll ch(int x,int v) {
    	return C[a[x]][v]*mi[b[x]*v];
    }
    ll f[35][3][3][5][2];
    ll wk1() {
    	memset(f,0,sizeof(f));
    	f[0][0][0][0][0]=1;
    	int i,j,k,x,y,z,w;
    	for(i=0;i<35;i++) {
    		for(j=0;j<3;j++) if(!j||(i<=27&&i%9!=0&&i%9!=1)) {
    			for(k=0;k<3;k++) if(!k||(i<=27&&i%9!=8&&i%9!=0)) if(a[i+1]>=j+k) {
    				for(x=j+k;x<=4;x++) {
    					for(y=0;y<2;y++) if(f[i][j][k][x][y]) {
    						for(z=0;z<=2&&j+k+z<=a[i+1]&&x+z<=4;z++) {
    							for(w=0;j+k+z+w*3<=a[i+1]&&x+z+w<=4;w++) {
    								int t=j+k+z+w*3;
    								upd(f[i+1][k][z][x+z+w][y],f[i][j][k][x][y]*ch(i+1,t));
    								if(!y&&t+2<=a[i+1]) upd(f[i+1][k][z][x+z+w][1],f[i][j][k][x][y]*ch(i+1,t+2));
    							}
    						}
    						if(a[i+1]-j-k==4&&x<4) upd(f[i+1][k][0][x+1][y],f[i][j][k][x][y]*ch(i+1,4));
    					}
    				}
    			}
    		}
    	}return f[34][0][0][4][1];
    }
    ll c[N];
    ll wk2() {
    	int i;
    	for(i=1;i<=34;i++)c[i]=ch(i,2);
    	sort(c+1,c+35);ll re=1;
    	for(i=28;i<=34;i++)re*=c[i];return re*7;
    }
    int d[20]={0,1,9,10,18,19,27,28,29,30,31,32,33,34};
    ll wk3() {
    	ll re=0;int i,j;
    	for(i=1;i<=13;i++) {
    		ll tmp=ch(d[i],2);for(j=1;j<=13;j++)if(i!=j)tmp*=ch(d[j],1);re=max(re,tmp);
    	}return re*13;
    }
    void solve() {
    	int i,j;
    	for(i=1;i<=34;i++) a[i]=4,b[i]=0;
    	for(mi[0]=i=1;i<=18;i++) mi[i]=mi[i-1]<<1;;
    	for(i=0;i<=4;i++)for(C[i][0]=j=1;j<=i;j++)C[i][j]=C[i-1][j]+C[i-1][j-1];
    	while(1) {
    		char w[10];
    		scanf("%s",w+1);
    		if(w[1]=='0') break;
    		a[trans(w)]--;
    	}
    	while(1) {
    		char w[10];
    		scanf("%s",w+1);
    		if(w[1]=='0') break;
    		b[trans(w)]=1;
    	}
    	printf("%lld
    ",max(wk1(),max(wk2(),wk3())));
    }
    int main() {
    	int T;
    	scanf("%d",&T);
    	while(T--) solve();
    }
    

    LOJ#3085. 「GXOI / GZOI2019」特技飞行

    https://loj.ac/problem/3085

    分析:

    • 注意到(c)(a,b)无关,直接数个点就行了。
    • 然后我们感性理解一下如果所有特技都「对向交换」,那么一定是合法的。
    • 于是就有了一个极值,另一个极值是要最小化「对向交换」的数量。
    • 考虑排序后的数组的每个置换,可以发现最少交换次数为置换大小-1,且不同置换之间不影响,那么这部分的数量就是n-置换数。
    • (bit还得拆成四个精度搞来搞去太麻烦,直接上kdt跑得还挺快)
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <set>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    typedef double f2;
    #define N 500050
    const f2 eps = 1e-10;
    int n,Y0[N],Y1[N],st,ed,is[N],tot,id[N],to[N],K,vis[N];
    ll A,B,C;
    char buf[100000],*p1,*p2;
    #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
    int rd() {
    	int x=0; char s=nc();
    	while(s<'0') s=nc();
    	while(s>='0') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    	return x;
    }
    set<pair<int,int> >S;
    set<pair<int,int> >::iterator it;
    f2 mn[N][2],mx[N][2];
    int flg[N],wh,ch[N][2],ok[N];
    struct Point {
    	f2 p[2];
    	Point() {}
    	Point(f2 x_,f2 y_) {p[0]=x_,p[1]=y_;}
    	bool operator < (const Point &u) const {
    		return p[wh]==u.p[wh]?p[!wh]<u.p[!wh]:p[wh]<u.p[wh];
    	}
    }a[N];
    bool cmp(int x,int y) {
    	return Y1[x]<Y1[y];
    }
    f2 Abs(f2 x) {return x>0?x:-x;}
    void pushup(int p,int x) {
    	mn[p][0]=min(mn[p][0],mn[x][0]);
    	mn[p][1]=min(mn[p][1],mn[x][1]);
    	mx[p][0]=max(mx[p][0],mx[x][0]);
    	mx[p][1]=max(mx[p][1],mx[x][1]);
    }
    
    int build(int l,int r,int f,int k) {
    	if(l>r) return 0;
    	int mid=(l+r)>>1;
    	wh=k;
    	nth_element(a+l,a+mid,a+r+1);
    	int p=mid;
    	mn[p][0]=mx[p][0]=a[p].p[0];
    	mn[p][1]=mx[p][1]=a[p].p[1];
    	if(l<mid) ch[p][0]=build(l,mid-1,p,!k),pushup(p,ch[p][0]);
    	if(r>mid) ch[p][1]=build(mid+1,r,p,!k),pushup(p,ch[p][1]);
    	return p;
    }
    void update(int x,int y,int z,int p) {
    	if(!p||flg[p]||x+z<mn[p][0]||x-z>mx[p][0]||y+z<mn[p][1]||y-z>mx[p][1]) return ;
    	//if(!p) return ;
    	if(max(max(max(abs(x-mn[p][0]),abs(x-mx[p][0])),abs(y-mn[p][1])),abs(y-mx[p][1]))-eps<z) {
    		flg[p]=1; return ;
    	}
    	if(!ok[p]&&max(abs(a[p].p[0]-x),abs(a[p].p[1]-y))-eps<z) {
    		ok[p]=1;
    	}
    	update(x,y,z,ch[p][0]); update(x,y,z,ch[p][1]);
    }
    int dfs(int p) {
    	if(flg[p]) {
    		ok[p]=1; 
    		ok[ch[p][0]]=ok[ch[p][1]]=flg[ch[p][0]]=flg[ch[p][1]]=1;
    	}
    	int re=ok[p];
    	if(ch[p][0]) re+=dfs(ch[p][0]);
    	if(ch[p][1]) re+=dfs(ch[p][1]);
    	return re;
    }
    int main() {
    	scanf("%d%lld%lld%lld%d%d",&n,&A,&B,&C,&st,&ed);
    	int i,j;
    	for(i=1;i<=n;i++) Y0[i]=rd();
    	for(i=1;i<=n;i++) Y1[i]=rd();
    	for(i=n;i;i--) {
    		for(it=S.begin();it!=S.end()&&(it->first < Y1[i]);it++) {
    			j=it->second;
    			f2 t=f2(Y0[j]-Y0[i])/(Y1[i]-Y1[j]);
    			f2 x=(t*ed+st)/(t+1);
    			f2 y=(t*Y1[j]+Y0[j])/(t+1);
    			f2 tx=x,ty=y;
    			x=tx+ty;
    			y=tx-ty;
    			a[++tot]=Point(x,y);
    		}
    		S.insert(make_pair(Y1[i],i));
    	}
    	int rt=build(1,tot,0,0);
    	K=rd();
    	for(i=1;i<=K;i++) {
    		int x,y,z;
    		x=rd(),y=rd(),z=rd();
    		int tx=x,ty=y;
    		x=tx+ty;
    		y=tx-ty;
    		update(x,y,z,rt);
    	}
    	ll ans=dfs(rt)*C;
    	ll ans1=ans+tot*A,ans2=ans+tot*A;
    	for(i=1;i<=n;i++) id[i]=i;
    	sort(id+1,id+n+1,cmp);
    	ll u=n;
    	for(i=1;i<=n;i++) if(!vis[i]) {
    		u--;
    		for(j=i;!vis[j];j=id[j]) vis[j]=1;
    	}
    	ans2+=(tot-u)*(B-A);
    	if(ans1>ans2) swap(ans1,ans2);
    	printf("%lld %lld
    ",ans1,ans2);
    }
    

    LOJ#3086. 「GXOI / GZOI2019」逼死强迫症

    https://loj.ac/problem/3086

    分析:

    • (f_{i,j})表示有(i)列,放了(j)(1 imes 1)的格子的方案数,特别的我们令(j=1)的情况下表示(i-1)列多出来一个格子。
    • 转移推推就可以了
    • (f_{i,0}=f_{i-1,0}+f_{i-2,0})
    • (f_{i,1}=f_{i-1,0}+f_{i-2,0}+f_{i-2,1})
    • (f_{i,2}=f_{i-1,2}+f_{i-2,2}+2f_{i-2,1})
    • 然后搞一个(6 imes 6)的矩阵矩乘一下就行了。
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    #include <queue>
    using namespace std;
    #define N 1000050
    #define mod 1000000007
    typedef long long ll;
    struct Mat {
    	ll a[6][6];
    	Mat() {memset(a,0,sizeof(a));}
    	Mat operator * (const Mat &u) const {
    		Mat re; int i,j,k;
    		for(i=0;i<6;i++)for(k=0;k<6;k++)for(j=0;j<6;j++) {
    			re.a[i][j]+=a[i][k]*u.a[k][j];
    		}
    		for(i=0;i<6;i++)for(j=0;j<6;j++)re.a[i][j]%=mod;
    		return re;
    	}
    }G[33];
    Mat qp(Mat x,int y) {
    	Mat I;
    	int i;
    	for(i=0;i<6;i++) I.a[i][i]=1;
    	for(i=0;i<=30;i++) if(y&(1<<i)) I=I*G[i];
    	return I;
    }
    int main() {
    	int i;
    	Mat x;
    	int t;
    	x.a[0][0]=x.a[0][1]=x.a[1][0]=x.a[2][0]=x.a[2][1]=x.a[2][3]=1;
    	x.a[3][2]=x.a[4][4]=x.a[4][5]=x.a[5][4]=1;
    	x.a[4][3]=2;
    	G[0]=x;
    	for(i=1;i<=30;i++) G[i]=G[i-1]*G[i-1];
    	scanf("%d",&t);
    	while(t--) {
    		int y;scanf("%d",&y);
    		if(y<=2) {puts("0"); continue;}
    		Mat t=qp(x,y-1);
    		ll ans=(t.a[4][0]+t.a[4][1]+t.a[4][2]);
    		printf("%lld
    ",ans%mod);
    	}
    }
    

    LOJ #3087. 「GXOI / GZOI2019」旅行者

    https://loj.ac/problem/3087

    题意:有向图,给K个点,求两两之间最短的最短路。

    分析:

    • 二进制分组,然后跑最短路即可。
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    #include <queue>
    using namespace std;
    #define N 100050
    #define M 600050
    char buf[100000],*p1,*p2;
    #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
    int rd() {
    	int x=0; char s=nc();
    	while(s<'0') s=nc();
    	while(s>='0') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    	return x;
    }
    typedef long long ll;
    int head[N],to[M],nxt[M],val[M],vis[N],n,cnt,S,m,K,a[N];
    ll dis[N];
    priority_queue<pair<ll,int> >q;
    inline void add(int u,int v,int w) {
    	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
    }
    void dij() {
    	int i;
    	memset(dis+1,0x3f,S<<3);
    	memset(vis+1,0,S<<2);
    	dis[S]=0;
    	q.push(make_pair(0,S));
    	while(!q.empty()) {
    		int x=q.top().second; q.pop(); 
    		if(vis[x]) continue;
    		vis[x]=1;
    		for(i=head[x];i;i=nxt[i]) if(dis[to[i]]>dis[x]+val[i]) {
    			dis[to[i]]=dis[x]+val[i];
    			q.push(make_pair(-dis[to[i]],to[i]));
    		}
    	}
    }
    void solve() {
    	n=rd(),m=rd(),K=rd();
    	int i,x,y,z,j;
    	S=n+1;
    	memset(head+1,0,S<<2);
    	cnt=0;
    	for(i=1;i<=m;i++) {
    		x=rd(),y=rd(),z=rd();
    		add(x,y,z);
    	}
    	for(i=1;i<=K;i++) a[i]=rd();
    	random_shuffle(a+1,a+K+1);
    	random_shuffle(a+1,a+K+1);
    	random_shuffle(a+1,a+K+1);
    	int tmp=cnt;
    	ll ans=1ll<<60;
    	for(i=0;i<5;i++) {
    		cnt=tmp;
    		for(j=1;j<=K;j++) if((j>>i)&1) add(S,a[j],0);
    		dij();
    		for(j=1;j<=K;j++) if(!((j>>i)&1)) ans=min(ans,dis[a[j]]);
    		head[S]=0;
    		
    		cnt=tmp;
    		for(j=1;j<=K;j++) if(!((j>>i)&1)) add(S,a[j],0);
    		dij();
    		for(j=1;j<=K;j++) if((j>>i)&1) ans=min(ans,dis[a[j]]);
    		head[S]=0;
    	}
    	printf("%lld
    ",ans);
    }
    int main() {
    	int T;
    	scanf("%d",&T);
    	while(T--)solve();
    }
    

    LOJ#3088. 「GXOI / GZOI2019」旧词

    https://loj.ac/problem/3088

    题意:

    一棵树,每次给出(x,y),求(sumlimits_{ile x}mathrm{dep(lca(i,y))^k})

    分析:

    • 询问按(x)排序,一个一个插。
    • (k=1)是个比较经典的问题,插入时(1)(x)(+1),查询时查(y)(1)的链和即可。
    • 分析本质,加的这个(1)就是(dep_x-dep_{fa_x})
    • 很容易把他扩展到(k​)任意的情况,每条边的权值就是(dep_x^K-dep_{fa_x}^K​)
    • 树剖+线段树搞一搞,时间复杂度(mathrm{O(nlog n^2)}​)
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    #include <queue>
    using namespace std;
    #define N 50050
    #define ls p<<1
    #define rs p<<1|1
    #define mod 998244353
    char buf[100000],*p1,*p2;
    #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
    int rd() {
    	int x=0; char s=nc();
    	while(s<'0') s=nc();
    	while(s>='0') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    	return x;
    }
    typedef long long ll;
    int head[N],to[N],nxt[N],fa[N],top[N],dep[N],siz[N],son[N],dfn[N],idf[N],cnt;
    int n,Q,K;
    ll h[N],w[N],sum[N<<2],tag[N<<2],sv[N<<2],ans[N];
    struct A {
    	int x,y,id;
    	bool operator < (const A &u) const {
    		return x<u.x;
    	}
    }q[N];
    ll qp(ll x,ll y) {
    	ll re=1;for(;y;y>>=1,x=x*x%mod)if(y&1)re=re*x%mod;return re;
    }
    inline void add(int u,int v) {
    	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
    }
    void d1(int x) {
    	int i;siz[x]=1;
    	for(i=head[x];i;i=nxt[i]) {
    		dep[to[i]]=dep[x]+1;
    		d1(to[i]);siz[x]+=siz[to[i]];
    		if(siz[to[i]]>siz[son[x]]) son[x]=to[i];
    	}
    }
    void d2(int x,int t) {
    	top[x]=t; int i; dfn[x]=++dfn[0]; idf[dfn[0]]=x;
    	if(son[x]) d2(son[x],t);
    	for(i=head[x];i;i=nxt[i]) if(to[i]!=son[x]) d2(to[i],to[i]);
    }
    void build(int l,int r,int p) {
    	if(l==r) {sv[p]=w[idf[l]]; return ;}
    	int mid=(l+r)>>1;
    	build(l,mid,ls),build(mid+1,r,rs);
    	sv[p]=(sv[ls]+sv[rs])%mod;
    }
    inline void giv(int p,ll d) {
    	sum[p]=(sum[p]+d*sv[p])%mod; tag[p]+=d;
    }
    inline void pushdown(int p) {
    	if(tag[p]) {
    		giv(ls,tag[p]),giv(rs,tag[p]); tag[p]=0;
    	}
    }
    void update(int l,int r,int x,int y,int p) {
    	if(x<=l&&y>=r) {giv(p,1); return ;}
    	int mid=(l+r)>>1;
    	pushdown(p);
    	if(x<=mid) update(l,mid,x,y,ls);
    	if(y>mid) update(mid+1,r,x,y,rs);
    	sum[p]=(sum[ls]+sum[rs])%mod;
    }
    ll query(int l,int r,int x,int y,int p) {
    	if(x<=l&&y>=r) return sum[p];
    	int mid=(l+r)>>1; ll re=0;
    	pushdown(p);
    	if(x<=mid) re=query(l,mid,x,y,ls);
    	if(y>mid) re=(re+query(mid+1,r,x,y,rs))%mod;
    	return re;
    }
    void fix(int x) {
    	for(;top[x]!=1;x=fa[top[x]]) {
    		update(1,n,dfn[top[x]],dfn[x],1);
    	}
    	update(1,n,1,dfn[x],1);
    }
    ll ask(int x) {
    	ll re=0;
    	for(;top[x]!=1;x=fa[top[x]]) {
    		re+=query(1,n,dfn[top[x]],dfn[x],1);
    	}re+=query(1,n,1,dfn[x],1);
    	return re%mod;
    }
    int main() {
    	n=rd(),Q=rd(),K=rd();
    	int i;
    	for(i=2;i<=n;i++) fa[i]=rd(),add(fa[i],i);
    	dep[1]=1,d1(1),d2(1,1);
    	for(i=1;i<=n;i++) h[i]=qp(i,K);
    	for(i=n;i;i--) h[i]=h[i]-h[i-1];
    	for(i=1;i<=n;i++) w[i]=(h[dep[i]]+mod)%mod;
    	build(1,n,1);
    	for(i=1;i<=Q;i++) {
    		q[i].x=rd(),q[i].y=rd(); q[i].id=i;
    	}
    	sort(q+1,q+Q+1);
    	int j=1;
    	for(i=1;i<=Q;i++) {
    		for(;j<=n&&j<=q[i].x;j++) {
    			fix(j);
    		}
    		ans[q[i].id]=ask(q[i].y);
    	}
    	for(i=1;i<=Q;i++) printf("%lld
    ",(ans[i]+mod)%mod);
    }
    

    总结:一场难度低于(mathrm{noip})的省选,(d1t3)的置换部分还是不错的。

  • 相关阅读:
    JavaScript表单验证年龄
    PHP会话处理相关函数介绍
    SpringCloud(第一天)
    springboot加强
    SpringBoot的第一个demo
    ElasticSearch(分布式全文搜索引擎)
    Redis集群
    NoSql和Redis
    ElementUI实现CRUD(修改前端页面),前后台解决跨域问题
    SSM+ElementUI综合练习和Swagger和postman的使用(第二天)
  • 原文地址:https://www.cnblogs.com/suika/p/10718214.html
Copyright © 2011-2022 走看看