csp9102

這裏放一下題解

day1

T1

根據題目描述遞歸,也可以使用n xor (n>>1)n\ xor\ (n>>1)這樣的神仙做法,注意unsigned long longunsigned\ long\ long即可。
時間複雜度:O(log(n))O(log(n))O(1)O(1)
當場代碼:

#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<time.h>
#include<bitset>
#include<vector>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(int i=pos[x];~i;i=e[i].next)
#define ll unsigned long long
#define db double
const int N=7;
const int INF=1e9+7;
ll n,k;

int main(){
	scanf("%llu%llu",&n,&k);
	for(ll i=n;i;i--){
		if(k<(((ll)1)<<((ll)(i-1))))putchar('0');
		else{
			k-=(((ll)1)<<((ll)(i-1)));
			k^=((((ll)1)<<((ll)(i-1)))-1);
			putchar('1');
		}
	}
	puts("");
	return 0;
}

T2

首先把左括號看成11,右括號看成1-1,做樹上前綴和。然後一個點如果是右括號,則存在以這個點爲結尾的括號序列,遇到這樣的點可以向上跳到第一個前綴和相同的點,當前點結尾的合法括號數爲這個點+1+1。用一個桶記錄,最後再樹上前綴和即可。
時間複雜度:O(n)O(n)
當場代碼:

#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<time.h>
#include<bitset>
#include<vector>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
void read(ll &x){
	char c=getchar(); x=0;
	while(c<'0'||c>'9')c=getchar();
	while(c<='9'&&c>='0')x=x*10ll+c-'0',c=getchar();
}
const ll N=5e5+7;
const ll INF=1e9+7;
char ss[N];
ll pos[N],fr[N*2],s[N],fa[N],up[N],d[N];
struct edge{ll v,next;}e[N];
ll n,num=0,ans=0;
void add(ll x,ll y){e[num]=(edge){y,pos[x]}; pos[x]=num++;}
void dfs1(ll x){
	if(ss[x]=='(')s[x]=s[fa[x]]+1;
	else s[x]=s[fa[x]]-1;
	up[x]=fr[s[x]+N];
	fr[s[x]+N]=x;
	repG(i,x)dfs1(e[i].v);
	fr[s[x]+N]=up[x];
}
void dfs2(ll x){
	if(s[fa[x]]<s[x])d[x]=0;
	else if(up[x]<0)d[x]=0;
	else d[x]=d[up[x]]+1;
	repG(i,x)dfs2(e[i].v);
}
void dfs3(ll x){
	d[x]+=d[fa[x]];
	ans^=(d[x]*x);
	repG(i,x)dfs3(e[i].v);
}
int main(){
	memset(fr,-1,sizeof(fr));
	memset(pos,-1,sizeof(pos));
	scanf("%lld",&n);
	scanf("%s",ss+1);
	REP(i,2,n){
		read(fa[i]);
		add(fa[i],i);
	}
	fr[N]=0;
	dfs1(1);
	dfs2(1);
	dfs3(1);
	printf("%lld\n",ans);
	return 0;
}

T3

基本思路是按位貪心,然後我們需要確定的事情就是當前能把一個數移動到哪些點。我們考慮確定了一個數的路徑以後會發生什麼,那就是對於路徑上相鄰的兩條邊(有公共點),對於它們的公共點來說,這兩條邊的操作順序是相鄰的。我們還發現如果一個點的所有出邊的操作順序確定合法了,那麼對於全局來說一定會有合法方案,所以我們就對於每個點的出邊維護一個鏈表,每次只需查詢能不能從一條邊進入一條邊走出,對於第一次操作和最後一次操作也維護一下即可。
時間複雜度:O(n2)O(n^2)
當場代碼:

#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<time.h>
#include<bitset>
#include<vector>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(int i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const int N=2007;
const int INF=1e9+7;
int T,n,num,mn;
int g[N],pos[N],ne[N*2],fr[N],dg[N];
struct edge{int v,next,ot;}e[N*2];
void add(int x,int y){e[num]=(edge){y,pos[x],1}; ne[num]=num; pos[x]=num++;}
void dfs1(int x,int eg){
	if(fr[x]==-1&&(ne[eg]!=-1||dg[x]==2))mn=min(mn,x);
	repG(i,x){
		if(!e[i].ot)continue;
		if(i==ne[eg])continue;
		if((fr[x]!=i||ne[eg]!=-1)||dg[x]==2)dfs1(e[i].v,i^1);
	}
}
bool dfs2(int x,int eg){
	if(fr[x]==-1&&(ne[eg]!=-1||dg[x]==2)){
		if(x==mn){
			fr[x]=ne[eg];
			dg[x]--;
			return 1;
		}
	}
	repG(i,x){
		if(!e[i].ot)continue;
		if(i==ne[eg])continue;
		if((fr[x]!=i||ne[eg]!=-1)||dg[x]==2){
			if(dfs2(e[i].v,i^1)){
				e[i].ot=0;
				if(fr[x]!=i){
					repG(j,x)if(ne[j]==i)ne[j]=ne[eg];
				}
				else fr[x]=ne[eg];
				dg[x]--;
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	scanf("%d",&T);
	while(T--){
		num=0;
		memset(pos,-1,sizeof(pos));
		scanf("%d",&n);
		rep(i,n)scanf("%d",&g[i]);
		rep(i,n)dg[i]=2,fr[i]=-1;
		rep(i,n-1){
			int x,y; scanf("%d%d",&x,&y);
			dg[x]++;
			dg[y]++;
			add(x,y); add(y,x);
		}
		rep(i,n){
			mn=INF;
			repG(j,g[i]){
				if(!e[j].ot)continue;
				if(fr[g[i]]!=j||dg[g[i]]==2)dfs1(e[j].v,j^1);
			}
			repG(j,g[i]){
				if(!e[j].ot)continue;
				if(fr[g[i]]!=j||dg[g[i]]==2){
					if(dfs2(e[j].v,j^1)){	
						e[j].ot=0;	
						if(fr[g[i]]!=j)repG(k,g[i])if(ne[k]==j)ne[k]=-1;
						dg[g[i]]--;
					}
				}
			}
			printf("%d ",mn);
		}
		puts("");
	}
	return 0;
}

day2

T1

我們發現不符合要求的食材最多隻有一種,於是可以先進行補集轉化,然後枚舉不符合要求的食材,這時我們可以把其它食材看成一種,做一個揹包,發現限制是兩種食材的差,所以令dpi,jdp_{i,j}表示前ii個烹飪方法,兩種食材差值爲jj的方案數,揹包即可。
時間複雜度:O(mn2)O(mn^2)
代碼:

#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<bitset>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const ll N=107;
const ll M=2007;
const ll INF=1e9+7;
const ll mod=998244353;
ll a[N][M],s[N],u[N],v[N],f[N][N*2];
ll n,m,ans=1;

int main(){
	scanf("%lld%lld",&n,&m);
	rep(i,n)rep(j,m)scanf("%lld",&a[i][j]);
	rep(i,n){
		s[i]=0;
		rep(j,m)s[i]=(s[i]+a[i][j])%mod;
		ans=ans*(s[i]+1)%mod;
	}
	ans--;
	rep(i,m){
		rep(j,n)u[j]=a[j][i];
		rep(j,n)v[j]=(s[j]-a[j][i]+mod)%mod;
		rep(j,n+1)rep0(k,N*2)f[j][k]=0;
		f[1][N]=1;
		rep(j,n){
			rep0(k,N*2){
				if(f[j][k]){
					f[j+1][k]=(f[j+1][k]+f[j][k])%mod;
					f[j+1][k+1]=(f[j+1][k+1]+f[j][k]*u[j])%mod;
					f[j+1][k-1]=(f[j+1][k-1]+f[j][k]*v[j])%mod;
				}
			}
		}
		rep(j,n)ans=(ans-f[n+1][j+N]+mod)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}

T2

經過觀察我們可以猜出一個性質,那就是最後一段最短的一定是更優的。粗略證明的話就是因爲代價是平方,而二次函數導數單調遞增,所以把後面的分給前面一定更優。然後我們令dpidp_i表示前ii個位置往前最短到dpidp_i存在合法劃分方案,轉移時可以用單調隊列優化。最後答案可能很大,這裏採用CRTCRT計算。
時間複雜度:O(n)O(n)
代碼:

#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<bitset>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const ll N=4e7+7;
const ll INF=1e9+7;
const ll kk=1ll<<30;
void read(ll &x){
	char c=getchar(); x=0;
	while(c<'0'||c>'9')c=getchar();
	while(c<='9'&&c>='0')x=x*10+c-'0',c=getchar();
}
ll Pow(ll x,ll y,ll mod){
	ll ans=1,now=x;
	while(y){
		if(y&1)ans=ans*now%mod;
		now=now*now%mod;
		y>>=1;
	}
	return ans;
}
const ll T=1000000000;
struct gg{
	ll x[4];
	friend bool operator < (gg a,gg b){
		for(ll i=3;i;i--)if(a.x[i]!=b.x[i])return a.x[i]<b.x[i];
		return a.x[0]<b.x[0];
	}
	friend gg operator * (gg a,gg b){
		gg c={{0,0,0,0}};
		rep0(i,4)rep0(j,4)if(i+j<4)c.x[i+j]+=a.x[i]*b.x[j];
		rep0(i,3){
			c.x[i+1]+=c.x[i]/T;
			c.x[i]%=T;
		}
		return c;
	}
	friend gg operator + (gg a,gg b){
		gg c;
		rep0(i,4)c.x[i]=a.x[i]+b.x[i];
		rep0(i,3){
			c.x[i+1]+=c.x[i]/T;
			c.x[i]%=T;
		}
		return c;
	}
	friend gg operator - (gg a,gg b){
		gg c;
		rep0(i,4)c.x[i]=a.x[i]-b.x[i];
		rep0(i,3){
			while(c.x[i]<0){
				c.x[i+1]--;
				c.x[i]+=T;
			}
		}
		return c;
	}
};
ll n,ty,tp=1;
ll s[N],v[N];
int t[N],fr[N];
void print(gg r){
	bool fl=0;
	for(ll i=3;~i;i--){
		if(fl)for(ll j=T/10;j;j/=10)putchar('0'+r.x[i]/j%10);
		else{
			if(r.x[i]){
				printf("%lld",r.x[i]);
				fl=1;
			}
		}
	}
	if(!fl)putchar('0');
}
int main(){
	scanf("%lld%lld",&n,&ty);
	if(!ty)rep(i,n)read(s[i]);
	else{
		ll X,Y,Z,m; scanf("%lld%lld%lld%lld%lld%lld",&X,&Y,&Z,&s[1],&s[2],&m);
		REP(i,3,n)s[i]=(X*s[i-1]+Y*s[i-2]+Z)%kk;
		ll ls=0;
		rep(i,m){
			ll l,r,p; read(p); read(l); read(r);
			REP(j,ls+1,p)s[j]=s[j]%(r-l+1)+l;
			ls=p;
		}
	}
	rep(i,n)s[i]+=s[i-1];
	t[1]=v[1]=0;
	ll p=1;
	rep(i,n){
		while(p<tp&&v[p+1]<=s[i])p++;
		fr[i]=t[p];
		ll vv=2*s[i]-s[fr[i]];
		while(v[tp]>vv)tp--;
		t[++tp]=i;
		v[tp]=vv;
		if(p>tp)p=tp;
	}
	if(!ty){
		ll ans=0;
		for(ll i=n;i;i=fr[i])ans+=(s[i]-s[fr[i]])*(s[i]-s[fr[i]]);
		printf("%lld\n",ans);
	}
	else{
		ll mod1=998244353,mod2=1000000007,mod3=1004535809,mod4=19260817;
		ll s1=0,s2=0,s3=0,s4=0,hh=0;
		for(ll i=n;i;i=fr[i],hh++){
			ll v1=(s[i]-s[fr[i]])%mod1,v2=(s[i]-s[fr[i]])%mod2;
			ll v3=(s[i]-s[fr[i]])%mod3,v4=(s[i]-s[fr[i]])%mod4;
			s1+=v1*v1;
			s2+=v2*v2;
			s3+=v3*v3;
			s4+=v4*v4;
			if(hh&8){
				s1%=mod1;
				s2%=mod2;
				s3%=mod3;
				s4%=mod4;
			}
		}
		s1%=mod1;
		s2%=mod2;
		s3%=mod3;
		s4%=mod4;
		ll u1=s1*Pow(mod2*mod3%mod1*mod4%mod1,mod1-2,mod1)%mod1;
		ll u2=s2*Pow(mod1*mod3%mod2*mod4%mod2,mod2-2,mod2)%mod2;
		ll u3=s3*Pow(mod1*mod2%mod3*mod4%mod3,mod3-2,mod3)%mod3;
		ll u4=s4*Pow(mod1*mod2%mod4*mod3%mod4,mod4-2,mod4)%mod4;
		gg m1=(gg){{mod1%T,mod1/T,0,0}},m2=(gg){{mod2%T,mod2/T,0,0}};
		gg m3=(gg){{mod3%T,mod3/T,0,0}},m4=(gg){{mod4%T,mod4/T,0,0}};
		gg k1=(gg){{u1%T,u1/T,0,0}},k2=(gg){{u2%T,u2/T,0,0}};
		gg k3=(gg){{u3%T,u3/T,0,0}},k4=(gg){{u4%T,u4/T,0,0}};
		gg ans=k1*m2*m3*m4+k2*m1*m3*m4+k3*m1*m2*m4+k4*m1*m2*m3;
		gg pp=m1*m2*m3*m4;
		while(pp<ans)ans=ans-pp;
		print(ans);
		return 0;
	}
	return 0;
}

T3

這題做法很多。
我們可以考慮對每一條邊計算子樹內的中心和子樹外的重心,子樹內很好處理,我們對樹進行樹剖,最後中心一定在重鏈上,且一定是重兒子答案的祖先,維護指針掃描就好了。對於邊上方的部分,我們可以先在根節點的重鏈上二分,根據是否子樹中包含當前邊分成兩段。如果二分到這條邊的祖先,還要考慮一下次重兒子的重鏈。
時間複雜度:O(nlog(n))O(nlog(n))
代碼:

#include<iostream>
#include<cstring>
#include<cassert>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const ll N=3e5+7;
const ll INF=1e9+7;
ll T,n,num,cnt,Ans=0;
ll pos[N],sz[N],top[N],fa[N],son[N],ind[N],v[N],w[N],mx[N],nx[N],ns[N],dw[N],p[N],dep[N];
struct edge{ll v,next;}e[N*2];
void add(ll x,ll y){e[num]=(edge){y,pos[x]}; pos[x]=num++;}
void dfs1(ll x,ll f,ll d){
	dep[x]=d;
	fa[x]=f;
	sz[x]=1;
	mx[x]=ns[x]=0;
	repG(i,x){
		if(e[i].v==f)continue;
		dfs1(e[i].v,x,d+1);
		sz[x]+=sz[e[i].v];
		if(sz[e[i].v]>mx[x]){
			ns[x]=son[x];
			nx[x]=mx[x];
			mx[x]=sz[e[i].v];
			son[x]=e[i].v;
		}
		else if(sz[e[i].v]>nx[x]){
			nx[x]=sz[e[i].v];
			ns[x]=e[i].v;
		}
	}
}
void dfs2(ll x,ll tp){
	top[x]=tp;
	v[++cnt]=n-sz[x];
	ind[x]=cnt;
	w[cnt]=x;
	dw[x]=x;
	if(sz[x]==1)return;
	dfs2(son[x],tp);
	dw[x]=dw[son[x]];
	repG(i,x)if(e[i].v!=son[x]&&e[i].v!=fa[x])dfs2(e[i].v,e[i].v);
}
void dfs3(ll x){
	if(x!=1){
		ll u=fa[x],k,ks;
		while(top[u]!=1)u=fa[top[u]];
		ll t=upper_bound(v+1,v+ind[u]+1,n-((n+sz[x]+1)/2))-v-1;
		ll q=w[t];
		if(son[q]==x||q!=u){
			if(2*nx[q]<n-sz[x]){
				k=q;
				ks=sz[q]-sz[x];
			}
			else {
				ll tt=upper_bound(v+ind[ns[q]],v+ind[dw[ns[q]]]+1,n-(n-sz[x]+1)/2)-v-1;
				k=w[tt];
				ks=sz[k];
			}
		}
		else{
			if(2*mx[q]<n-sz[x]){
				k=q;
				ks=sz[q]-sz[x];
			}
			else{
				ll tt=upper_bound(v+ind[u]+1,v+ind[dw[u]]+1,n-(n-sz[x]+1)/2)-v-1;
				k=w[tt];
				ks=sz[k];
			}
		}
		Ans+=k;
		if(ks*2==n-sz[x])Ans+=fa[k];
	}
	if(sz[x]==1){
		p[x]=x;
		Ans+=x;
		return;
	}
	repG(i,x)if(e[i].v!=fa[x])dfs3(e[i].v);
	if(x!=1){
		p[x]=p[son[x]];
		while(sz[p[x]]*2<sz[x])p[x]=fa[p[x]];
		if(sz[p[x]]*2==sz[x])Ans+=fa[p[x]];
		Ans+=p[x];
	}
}
int main(){
	scanf("%lld",&T);
	rep(o,T){
		num=cnt=Ans=0;
		memset(pos,-1,sizeof(pos));
		scanf("%lld",&n);
		rep(i,n-1){
			ll x,y; scanf("%lld%lld",&x,&y);
			add(x,y); add(y,x);
		}
		dfs1(1,0,1);
		dfs2(1,1);
		dfs3(1);
		printf("%lld\n",Ans);
	} 
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章