2020統一省選A卷題解

D1T1

線段樹上二分。有一些需要注意的地方,二分時去往一邊時要記下另一邊端點處的答案。因爲要取溫度的最高值,需要找到答案後再找到最靠右的火戰士和等於答案中的火戰士和的位置。

Code:

#include<bits/stdc++.h>
#define maxn 2000005
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
template<class T>void write(T x){
	if(x>=10) write(x/10);
	putchar(x%10+48);
}
int Q,N,op[maxn],typ[maxn],X[maxn],Y[maxn],A[maxn];
int s[maxn<<2][2],val[maxn][2];
void ins(int x,int y,int t){
	for(int mid,i=1,l=1,r=N;;){
		s[i][t]+=y; if(l==r) return;
		mid=(l+r)>>1,x<=mid?(r=mid,i<<=1):(l=mid+1,i=i<<1|1);
	}
}
struct node{
	int mn,vr;
	node(int l=0,int r=0):mn(2*min(l,r)),vr(r){}
	bool operator < (const node &p)const{return mn==p.mn?vr>p.vr:mn<p.mn;}
};
node qry(int i,int l,int r,int v0,int v1){
	if(l==r) return node(v0+s[i][0],v1+s[i][1]);
	int mid=(l+r)>>1,vl=v0+s[i<<1][0],vr=v1+s[i<<1|1][1];
	if(vl<=vr+val[mid][1]) return max(node(vl,vr+val[mid][1]),qry(i<<1|1,mid+1,r,vl,v1));
	else return max(node(vl+val[mid+1][0],vr),qry(i<<1,l,mid,v0,vr));
}
int find(int vr){
	for(int mid,i=1,l=1,r=N;;){
		if(l==r) return l;
		mid=(l+r)>>1,s[i<<1|1][1]>=vr?(i=i<<1|1,l=mid+1):(r=mid,vr-=s[i<<1|1][1],i<<=1);
	}
}
int main()
{
	read(Q);
	for(int i=1;i<=Q;i++){
		read(op[i]),read(typ[i]);
		if(op[i]==1) read(X[i]),read(Y[i]),A[++N]=X[i];
	}
	sort(A+1,A+1+N),N=unique(A+1,A+1+N)-A-1;
	for(int i=1;i<=Q;i++){
		if(op[i]==1) {X[i]=lower_bound(A+1,A+1+N,X[i])-A, ins(X[i],Y[i],typ[i]),val[X[i]][typ[i]]+=Y[i];}
		else {int k=typ[i]; ins(X[k],-Y[k],typ[k]),val[X[k]][typ[k]]-=Y[k];}
		node ans=qry(1,1,N,0,0);
		if(!ans.mn) puts("Peace");
		else write(A[find(ans.vr)]),putchar(' '),write(ans.mn),putchar('\n');
	}
}

D1T2

k=0nf(k)xk(nk)=k=0ni=0maikixk(nk)=i=0maik=0nkixk(nk)=i=0maik=0nj=0i{ij}(kj)j!xk(nk)=i=0maij=0i{ij}j!k=0n(kj)xk(nk)=i=0maij=0i{ij}j!k=0n(nj)(njkj)xk=i=0maij=0i{ij}j!(nj)k=0n(njkj)xk=i=0maij=0i{ij}j!(nj)xjk=0nj(njk)xk=i=0maij=0i{ij}j!(nj)xj(x+1)nj=i=0maij=0i{ij}njxj(x+1)nj\sum_{k=0}^nf(k)*x^k*\binom nk\\ =\sum_{k=0}^n\sum_{i=0}^ma_ik^ix^k\binom nk\\ =\sum_{i=0}^ma_i\sum_{k=0}^nk^ix^k\binom nk\\ =\sum_{i=0}^ma_i\sum_{k=0}^n\sum_{j=0}^i\begin{Bmatrix}i\\j\end{Bmatrix}\binom kjj!*x^k\binom nk\\ =\sum_{i=0}^ma_i\sum_{j=0}^i\begin{Bmatrix}i\\j\end{Bmatrix}j!\sum_{k=0}^n\binom kjx^k\binom nk\\ =\sum_{i=0}^ma_i\sum_{j=0}^i\begin{Bmatrix}i\\j\end{Bmatrix}j!\sum_{k=0}^n\binom nj\binom {n-j}{k-j}x^k\\ =\sum_{i=0}^ma_i\sum_{j=0}^i\begin{Bmatrix}i\\j\end{Bmatrix}j!\binom nj\sum_{k=0}^n\binom {n-j}{k-j}x^k\\ =\sum_{i=0}^ma_i\sum_{j=0}^i\begin{Bmatrix}i\\j\end{Bmatrix}j!\binom njx^j\sum_{k=0}^{n-j}\binom {n-j}kx^k\\ =\sum_{i=0}^ma_i\sum_{j=0}^i\begin{Bmatrix}i\\j\end{Bmatrix}j!\binom njx^j*(x+1)^{n-j}\\ =\sum_{i=0}^ma_i\sum_{j=0}^i\begin{Bmatrix}i\\j\end{Bmatrix}n^{\underline j}*x^j*(x+1)^{n-j}

Upd:洛谷有模質數下O(mlogm)O(m\log m)的簡單做法
在這裏插入圖片描述

Code:

#include<bits/stdc++.h>
#define maxn 1005
using namespace std;
int n,x,p,m,a[maxn],ans,S[maxn][maxn],pn[maxn],px[maxn],fn[maxn];
int Pow(int a,int b,const int mod = p){
	int s=1;for(;b;b>>=1,a=1ll*a*a%mod) b&1&&(s=1ll*s*a%mod); return s;
}
int main()
{
	scanf("%d%d%d%d",&n,&x,&p,&m); const int mod = p;
	for(int i=0;i<=m;i++) scanf("%d",&a[i]);
	for(int i=S[0][0]=1;i<=m;i++)
		for(int j=1;j<=i;j++)
			S[i][j]=(S[i-1][j-1]+1ll*j*S[i-1][j])%mod;
	px[0]=1; for(int i=1;i<=m;i++) px[i]=1ll*px[i-1]*x%mod;
	fn[0]=1; for(int i=1;i<=m;i++) fn[i]=1ll*fn[i-1]*(n-i+1)%mod;
	int t=min(m,n); pn[t]=Pow(x+1,n-t); for(int i=t-1;i>=0;i--) pn[i]=1ll*pn[i+1]*(x+1)%mod;
	for(int i=0;i<=m;i++){
		int ret=0;
		for(int j=0;j<=i&&j<=n;j++)
			ret=(ret+1ll*S[i][j]*fn[j]%mod*px[j]%mod*pn[j])%mod;
		ans=(ans+1ll*a[i]*ret)%mod;
	}
	printf("%d\n",ans);
}

D1T3

枚舉A中的每個數,刪去,如果剩下的數此時能夠加入線性基,就形成了刪去的數與它的偏序關係。
因爲給出的是最大的線性基,所以兩個不同的可以通過一個一個替換得到。
然後就是最小化平方代價,保序迴歸模板,整體二分取整問題後在midmid一邊的改變代價取爲(x(mid+1))2(xmid)2(x-(mid+1))^2-(x-mid)^2,另一邊同理,然後最小割,劃分。

Code:

#include<bits/stdc++.h>
#define maxn 1005
#define maxm 260005
#define LL long long
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
using namespace std;
typedef unsigned long long ULL;
const LL inf = 0x3f3f3f3f3f3f3f3fll;
int S,T,dis[maxn];
int fir[maxn],cur[maxn],nxt[maxm],to[maxm],tot=1; LL c[maxm];
void line(int x,int y,LL z){
	nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,c[tot]=z;
	nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,c[tot]=0;
}
bool BFS(){
	memset(dis,-1,(T+1)<<2); static int q[maxn],l,r; dis[q[l=r=1]=T]=0;
	while(l<=r){
		int u=q[l++];
		for(int i=fir[u],v;i;i=nxt[i]) if(c[i^1]&&dis[v=to[i]]==-1) dis[q[++r]=v]=dis[u]+1;
	}
	return ~dis[S];
}
LL aug(int u,LL augco){
	if(u==T) return augco;
	LL need=augco,dt;
	for(int &i=cur[u],v;i;i=nxt[i]) if(c[i]&&dis[v=to[i]]+1==dis[u]){
		dt=aug(v,min(c[i],need)),c[i]-=dt,c[i^1]+=dt;
		if(!(need-=dt)) return augco;
	}
	return augco-need;
}
struct Base{
	ULL d[65];
	void init(){memset(d,0,sizeof d);}
	void ins(ULL x) {for(int i=63;i>=0;i--) if(x>>i&1) {if(d[i]) x^=d[i]; else return void(d[i]=x);}}
	bool chk(ULL x) {for(int i=63;i>=0;i--) if(x>>i&1) {if(d[i]) x^=d[i]; else return 1;} return 0;}
}X;
int n,m,V[maxn],A[maxn],B[maxn],q[maxn],pos[maxn],tq[maxn];
ULL C[maxn];
LL ans;
bool inA[maxn],inB[maxn];
struct Edge{int x,y;}E[maxm],tE[maxm]; int cnt;
LL sqr(int x){return 1ll*x*x;}
void solve(int l,int r,int el,int er,int vl,int vr){
	if(l>r) return;
	if(vl==vr) {rep(i,l,r) ans+=sqr(V[q[i]]-vl); return;}
	int mid=(vl+vr)>>1;
	S=0,T=r-l+2,memset(fir,0,(T+1)<<2),tot=1;
	rep(o,1,r-l+1){
		int i=q[l+o-1]; pos[i]=o;
		V[i]<=mid?line(o,T,sqr(mid+1-V[i])-sqr(mid-V[i])):line(S,o,sqr(mid-V[i])-sqr(mid+1-V[i]));
	}
	rep(i,el,er) line(pos[E[i].x],pos[E[i].y],inf);
	while(BFS()) memcpy(cur,fir,(T+1)<<2),aug(S,inf);
	int l1=l,r1=r,l2=el,r2=er;
	rep(i,1,r-l+1) tq[~dis[i]?l1++:r1--]=q[l+i-1];
	rep(i,el,er){
		if(~dis[pos[E[i].x]]&&~dis[pos[E[i].y]]) tE[l2++]=E[i];
		if(dis[pos[E[i].x]]==-1&&dis[pos[E[i].y]]==-1) tE[r2--]=E[i];
	}
	rep(i,l,r) q[i]=tq[i]; rep(i,el,er) E[i]=tE[i];
	solve(l,l1-1,el,l2-1,vl,mid),solve(r1+1,r,r2+1,er,mid+1,vr);
}

int main()
{
	scanf("%d%d",&n,&m);
	rep(i,1,n) scanf("%llu",&C[i]);
	rep(i,1,n) scanf("%d",&V[i]);
	rep(i,1,m) scanf("%d",&A[i]),inA[A[i]]=1;
	rep(i,1,m) scanf("%d",&B[i]),inB[B[i]]=1;
	rep(i,1,m){
		X.init(); rep(j,1,m) if(i!=j) X.ins(C[A[j]]);
		rep(j,1,n) if(!inA[j]&&X.chk(C[j])) E[++cnt]=(Edge){A[i],j};
		X.init(); rep(j,1,m) if(i!=j) X.ins(C[B[j]]);
		rep(j,1,n) if(!inB[j]&&X.chk(C[j])) E[++cnt]=(Edge){j,B[i]};
	}
	rep(i,1,n) q[i]=i;
	solve(1,n,1,cnt,*min_element(V+1,V+1+n),*max_element(V+1,V+1+n));
	printf("%lld\n",ans);
}

D2T1

對於兩個信號站 x,yx,y,記 cnt[x][y]cnt[x][y] 表示 xxyy 傳遞信號的次數,p[x]p[x]xx 在排列中的位置。那麼答案爲:
x,ycnt[x][y](p[x]p[y] ? p[y]p[x] : p[x]k+p[y]k)\sum_{x,y}cnt[x][y]*(p[x]\le p[y]~?~p[y]-p[x]~:~p[x]*k+p[y]*k)

容易發現 yy 的貢獻只與 p[x]p[x]p[y]p[y] 的大小關係有關,而與 xx 內部的順序無關,所以記狀態 f[i][S]f[i][S] 表示前 ii 個位置已經放的信號站的狀態爲 SS,已經確定的貢獻的最小值。已經確定的貢獻就是還未放入的點與已經確定的點的貢獻中已經確定的部分,以及已經確定的點內部的部分。

實際上第一維可以去掉,直接從小到大枚舉狀態。

轉移需要求出每個不在SS中的點與在SS中的點的傳遞次數之和(作爲左邊和右邊都要求),這個可以用2mm2^m*m預處理,但是會 MLE\texttt{MLE}

將從小到大枚舉狀態看做是在01Trie樹上按dfs序走,那麼每次SS只會增加1個1,可以O(m)O(m)更新我們需要求的值,這樣的話就只需要把每一層的值存下來,回退,更新即可。這樣求這個值的空間複雜度就是O(m2)O(m^2)

然後枚舉下一個點放哪一個更新即可,時間複雜度O(2mm)O(2^m*m),空間複雜度O(2m+m2)O(2^m+m^2)

Upd:在洛谷看到一個提交是預處理點到左半和右半狀態,然後轉移時相加,預處理時順便可以處理係數,空間複雜度O(2m+2m2m2)O(2^m+2m*2^{\frac m2}),常數優秀。

Code:

#include<bits/stdc++.h>
using namespace std;
int n,m,k,f[1<<23],cnt[23][23],bit[1<<23],lg[1<<23];
int SL[23],SR[23],L[24][23],R[24][23],q[24],tp;
int main()
{
	scanf("%d%d%d",&n,&m,&k); int pre; scanf("%d",&pre),pre--;
	for(int i=2,now;i<=n;i++,pre=now) scanf("%d",&now),now--,now!=pre&&(cnt[pre][now]++,SL[pre]++,SR[now]++);
	memset(f,0x3f,sizeof f),f[0]=0;
	for(int i=0;i<m;i++) lg[1<<i]=i;
	for(int s=0,x;s<1<<m;s++){
		if(s){
			while(tp&&(q[tp]&s)!=q[tp]) tp--;
			x=lg[s&-s]; q[++tp]=s;
			for(int i=0;i<m;i++) L[tp][i]=L[tp-1][i]+cnt[i][x],R[tp][i]=R[tp-1][i]+cnt[x][i];
		}
		bit[s]=bit[s>>1]+(s&1),x=bit[s]+1;
		for(int i=0;i<m;i++) if(!(s>>i&1))
			f[s|1<<i]=min(f[s|1<<i],f[s]+x*(L[tp][i]*k+R[tp][i]-(SL[i]-L[tp][i])+(SR[i]-R[tp][i])*k));
	}
	printf("%d\n",f[(1<<m)-1]);
}

D2T2

就是子樹+1,維護異或和,合併,從低到高位建Trie樹,x xor (x+1)=2x0+11x~xor~(x+1)=2^{x末尾0的位置+1}-1,+1就是翻轉左右兒子,然後繼續遞歸左兒子,順便維護答案。

Code:

#include<bits/stdc++.h>
#define maxn 525505
#define maxp maxn*25
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,a[maxn],f[maxn];
int fir[maxn],nxt[maxn];
void line(int x,int y){nxt[y]=fir[x],fir[x]=y;}
int ch[maxp][2],cnt[maxp],rt[maxn],sz;
void ins(int &i,int v,int d){
	if(!i) i=++sz;
	cnt[i]++;
	if(d==22) return;
	ins(ch[i][v>>d&1],v,d+1);
}
void mdf(int i,int &f,int d){
	if(!i) return;
	f^=cnt[ch[i][0]]&1?((1<<(d+1))-1):0;
	swap(ch[i][0],ch[i][1]);
	mdf(ch[i][0],f,d+1);
}
void merge(int &i,int x,int y){
	if(!x||!y) return void(i=x+y);
	i=x,cnt[i]=cnt[x]+cnt[y];
	if(ch[x][0]||ch[x][1]) merge(ch[i][0],ch[x][0],ch[y][0]),merge(ch[i][1],ch[x][1],ch[y][1]);
}
void dfs(int u){
	for(int v=fir[u];v;v=nxt[v])
		dfs(v),f[u]^=f[v],mdf(rt[v],f[u],0),merge(rt[u],rt[u],rt[v]);
	ins(rt[u],a[u],0),f[u]^=a[u];
}
int main()
{
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=2,x;i<=n;i++) read(x),line(x,i);
	dfs(1);
	long long ans=0;
	for(int i=1;i<=n;i++) ans+=f[i];
	printf("%lld\n",ans);
}

D2T3

直接求出wwdd的倍數的生成樹的邊權和然後莫比烏斯反演求出gcd=dgcd=d的生成樹邊權和就可以了。

總邊數是O(E=mdivisor(wi))O(E=m*\sum divisor(w_i))的,divisor(w)divisor(w)最大是144,剩下的複雜度取決於怎麼求生成樹的邊權和。

如果枚舉一條邊刪掉求不包含這條邊的生成樹個數,這個複雜度是O(n3E)O(n^3E)的,加上map套vector檢驗邊集是否相同剪枝後似乎可以通過(我考場寫的這個)。

考慮矩陣樹的邊權形式是邊權之積,那麼我們如果把其中一條邊的權值改爲 wixw_ix,那麼行列式的 [x1][x^1] 係數就是包含這條邊的生成樹個數wi*w_i,如果把所有的邊都換成 1+wix1+w_ix,那麼 [x1][x^1] 係數就是所有生成樹的邊權和,只需要在模 x2x^2 意義下求行列式即可。

ax+bax+bx2x^2 意義下的逆元是 baxb2\frac {b-ax}{b^2},如果某一行 b2b^2 全爲 0 就相當於整體除 xx 後再消。求一次行列式複雜度O(n3)O(n^3)。對於某個 dd,只有 ww 是它的倍數的邊數 n1\ge n-1 的時候才做,最多做 En1\frac E{n-1} 次,總複雜度 O(n2E)=O(n2mmaxdiv(w))O(n^2E)=O(n^2m\max div(w))

代碼中高斯消元部分並沒有當前位爲0時進行交換的操作,這似乎是矩陣樹定理的特殊性質(對角線必定有值)
Code:

#include<bits/stdc++.h>
#define maxn 35
#define maxm maxn*maxn/2
#define maxW 152505
using namespace std;
const int mod = 998244353;
int n,m,X[maxm],Y[maxm],W[maxm],ans,Mx;
int Pow(int a,int b){int s=1;for(;b;b>>=1,a=1ll*a*a%mod) b&1&&(s=1ll*s*a%mod);return s;}
int upd(int x){return x+(x>>31&mod);}
struct node{
	int a,b;//ax+b
	node(int a=0,int b=0):a(a),b(b){}
	node operator + (const node &p)const{return node(upd(a+p.a-mod),upd(b+p.b-mod));}
	node operator - (const node &p)const{return node(upd(a-p.a),upd(b-p.b));}
	node operator * (const int &t)const{return node(1ll*a*t%mod,1ll*b*t%mod);}
	node operator * (const node &p)const{return node((1ll*a*p.b+1ll*b*p.a)%mod,1ll*b*p.b%mod);}
}A[maxn][maxn];
int Gauss(){
	node ret(0,1);
	for(int i=1;i<n;i++){
		if(!A[i][i].b) return 0;
		ret=ret*A[i][i];
		node inv=node(mod-A[i][i].a,A[i][i].b)*Pow(A[i][i].b,mod-3);
		for(int j=i+1;j<n;j++) if(A[j][i].a||A[j][i].b){
			node t=A[j][i]*inv;
			for(int k=i;k<n;k++) A[j][k]=A[j][k]-t*A[i][k];
		}
	}
	return ret.a;
}
vector<int>G[maxW]; int g[maxW],f[maxW];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d%d%d",&X[i],&Y[i],&W[i]),Mx=max(Mx,W[i]);
	for(int i=1;i<=m;i++){
		int x=W[i];
		for(int j=1;j*j<=x;j++) if(x%j==0){
			G[j].push_back(i);
			if(j*j!=x) G[x/j].push_back(i);
		}
	}
	for(int d=1,lim;d<=Mx;d++) if((lim=G[d].size())>=n-1){
		memset(A,0,sizeof A);
		for(int i=0;i<lim;i++){
			int x=X[G[d][i]],y=Y[G[d][i]],w=W[G[d][i]];
			A[x][x]=A[x][x]+node(w,1),A[y][y]=A[y][y]+node(w,1);
			A[x][y]=A[x][y]-node(w,1),A[y][x]=A[y][x]-node(w,1);
		}
		g[d]=Gauss();
	}
	for(int i=Mx;i>=1;i--){
		f[i]=g[i];
		for(int j=i+i;j<=Mx;j+=i) f[i]=upd(f[i]-f[j]);
		ans=(ans+1ll*f[i]*i)%mod;
	}
	printf("%d\n",ans);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章