2022-2023 ICPC Brazil Subregional Programming Contest(B,D,F,I,L,N)

发布时间 2023-07-04 02:06:03作者: QuantAsk

正题

题目链接:https://codeforces.com/gym/103960


B-Fun with Stones【博弈论,数位dp】

题目大意

三个堆的取石子游戏,第 \(i\) 个堆石子个数可能是 \([L_i,R_i]\),求先手必胜的概率。

\(1\leq L_i\leq R_i\le 10^9\)

解题思路

状压一下三个堆数量是否到达上界,就可以数位\(dp\)出界都是\([1,R_i]\)的先手必胜方案数。

然后当个三维的容斥一下就好了。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define ll long long
using namespace std;
const ll P=1e9+7;
const ll w[4]={0,3,5,6};
ll L1,R1,L2,R2,L3,R3,f[31][8];
ll read(){
	ll x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
void print(ll x)
{if(x>9)print(x/10);putchar(x%10+'0');return;}
ll power(ll x,ll b){
	ll ans=1;
	while(b){
		if(b&1)ans=ans*x%P;
		x=x*x%P;b>>=1;
	}
	return ans;
}
ll solve(ll n,ll m,ll k){
	memset(f,0,sizeof(f));f[30][7]=1;
	for(ll i=29;i>=0;i--){
		ll a=((n>>i)&1)+((m>>i)&1)*2+((k>>i)&1)*4;
		for(ll s=0;s<8;s++)
			for(ll z=0;z<4;z++)
				if(!((a^7)&s&w[z]))
					(f[i][s^(s&a&(w[z]^7))]+=f[i+1][s])%=P;
	}
	ll ans=0;
	for(ll s=0;s<8;s++)(ans+=f[0][s])%=P;
	return ans;
}
signed main()
{
	L1=read();R1=read();L2=read();R2=read();L3=read();R3=read();
	ll S=(R1-L1+1)*(R2-L2+1)%P*(R3-L3+1)%P,L=0;
	(L+=solve(R1,R2,R3))%=P;
	(L-=solve(L1-1,R2,R3))%=P;
	(L-=solve(R1,L2-1,R3))%=P;
	(L-=solve(R1,R2,L3-1))%=P;
	(L+=solve(L1-1,L2-1,R3))%=P;
	(L+=solve(L1-1,R2,L3-1))%=P;
	(L+=solve(R1,L2-1,L3-1))%=P;
	(L-=solve(L1-1,L2-1,L3-1))%=P;
	L=(L+P)%P;
	print((S-L+P)%P*power(S,P-2)%P);putchar('\n');
	return 0;
}

D-Displacing Particles【结论】

题目大意

开始有一个点在 \((2^{n-1},2^{n-1})\) ,然后你每次可以从 \((0,0),(0,2^n),(2^n,0),(2^n,2^n)\) 选择一个点让那个点变为他们的中点,求到达 \((x,y)\) 的最少次数。

\(1\leq n\leq 20,0<x,y<2^n\)

解题思路

相当于开始最高位有一个 \(1\) ,然后每次除以2再给最高位配一个数,所以求每个 \(x,y\) 的第一个 \(1\) 的位置然后取 \(max\) 就可以得到答案了。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,x,y,a,b;
int main()
{
	scanf("%d%d%d",&n,&x,&y);
	while(!(x&1))a++,x>>=1;
	while(!(y&1))b++,y>>=1;
	a=n-a;b=n-b;
	printf("%d",max(a,b)-1);
	return 0;
}


F-Multidimensional Hangman

题目大意

给出 \(n\) 个长度为 \(m\) 的小写字母字符串,其中都有一个字母不确定。

求一个字典序最小的能够匹配最多字符串的串。

\(1\leq n\leq 10^4,1\leq m\leq 12\)

解题思路

暴力枚举那个不确定的字母,然后压缩成long long用map存一下,遍历一遍就可以得到答案了。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define llu unsigned long long
using namespace std;
int n,m;
llu pw[20];char s[20];
map<llu,int> mp;
map<llu,int>::iterator it;
int main()
{
	scanf("%d%d",&n,&m);pw[0]=1;
	for(int i=1;i<=m;i++)pw[i]=pw[i-1]*26ull;
	for(int i=1;i<=n;i++){
		scanf("%s",s);int pos=0;
		llu h=0;
		for(int j=0;j<m;j++){
			h=h*26ull;
			if(s[j]=='*')pos=m-j;
			else h+=s[j]-'a';
		}
		for(int j=0;j<26;j++)
			mp[h+j*pw[pos-1]]++;
	}
	it=mp.begin();
	llu ans=0;int mx=0;
	while(it!=mp.end()){
		if((*it).second>mx)
			mx=(*it).second,ans=(*it).first;
		it++;
	}
	for(int i=m-1;i>=0;i--)
		putchar(ans/pw[i]%26+'a');
	putchar(' ');
	printf("%d\n",mx);
	return 0;
}

I-Intercepting Information

题目大意

输入 \(8\) 个数字,求是否有 \(9\)

解题思路

如题

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int x,flag;
int main()
{
	for(int i=1;i<=8;i++){
		scanf("%d",&x);
		if(x==9)flag=1;
	}
	if(flag)puts("F");
	else puts("S");
	return 0;

L-Listing Tedious Paths【根号分治,树上差分,LCA】

题目大意

给出 \(n\) 个点的一棵树,每个点有个颜色,求经过每一条边的,且首尾是相同颜色的路径数量。

\(2\leq n\leq 10^5\)

解题思路

根号分治一下,如果一种颜色数量超过 \(\sqrt n\) ,就整棵树 \(dp\) 一下更新答案,否则就暴力给每个点对的链加值。

给链加值的时候用树上差分,需要 \(O(1)\)\(LCA\) 保证复杂度。

时间复杂度:\(O(n\sqrt n)\)

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int N=2e5+10;
struct node{
	int to,next,w;
}a[N<<1];
int n,Q,tot,cnt,dfn[N],rfn[N],dep[N],lg[N];
int f[N][23],ls[N],pos[N],c[N],w[N];
long long ans[N],val[N];
vector<int> v[N];
void print(long long x)
{if(x>9)print(x/10);putchar(x%10+'0');return;}
void addl(int x,int y,int w){
	a[++tot].to=y;
	a[tot].next=ls[x];
	a[tot].w=w;
	ls[x]=tot;return;
}
void dfs(int x,int fa){
	dfn[++cnt]=x;rfn[x]=cnt;
	dep[x]=dep[fa]+1;
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==fa)continue;
		dfs(y,x);pos[y]=a[i].w;
		dfn[++cnt]=x;
	}
	return;
}
void init(){
	dfs(1,1);
	for(int i=1;i<=cnt;i++)f[i][0]=dfn[i];
	for(int i=2;i<=cnt;i++)lg[i]=lg[i/2]+1;
	for(int j=1;(1<<j)<=cnt;j++)
		for(int i=1;i+(1<<j)-1<=cnt;i++){
			int x=f[i][j-1],y=f[i+(1<<j-1)][j-1];
			f[i][j]=dep[x]<dep[y]?x:y;
		}
	return;
}
int LCA(int x,int y){
	int l=rfn[x],r=rfn[y];
	if(l>r)swap(l,r);
	int z=lg[r-l+1];
	x=f[l][z];y=f[r-(1<<z)+1][z];
	return dep[x]<dep[y]?x:y;
}
void solve(int x,int fa,int col){
	w[x]=(c[x]==col);
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==fa)continue;
		solve(y,x,col);w[x]+=w[y];
		ans[pos[y]]+=1ll*w[y]*(v[col].size()-w[y]);
	}
	return;
}
void calc(int x,int fa){
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==fa)continue;
		calc(y,x);val[x]+=val[y];
	}
	ans[pos[x]]+=val[x];
	return;
}
int main()
{
	scanf("%d",&n);Q=sqrt(n);
	for(int i=1;i<=n;i++){
		scanf("%d",&c[i]);
		v[c[i]].push_back(i);
	}
	for(int i=1,x,y;i<n;i++){
		scanf("%d%d",&x,&y);
		addl(x,y,i);addl(y,x,i);
	}
	init();
	for(int i=1;i<=n;i++){
		if(v[i].size()<=Q){
			for(int x=0;x<v[i].size();x++)
				for(int y=x+1;y<v[i].size();y++)
					val[v[i][x]]++,val[v[i][y]]++,
					val[LCA(v[i][x],v[i][y])]-=2;
		}
		else solve(1,0,i);
	}
	calc(1,0);
	for(int i=1;i<n;i++)print(ans[i]),putchar((i==n-1)?'\n':' ');
	return 0;
}

N-Numbers on both Sides【线段树】

题目大意

\(n\) 张牌排成一排,有正面反面的数值,选择首尾加起来 \(K\) 张牌取得正面的数值,然后在这些牌中选择 \(L\) 张取反面的数值,求最大数值和。

\(1\leq L\leq K\leq n\leq 10^5\)

解题思路

直接暴力枚举头选多少张尾选多少张,然后用线段树维护一下反面的数字,每次取前 \(L\) 大就好了。

时间复杂度:\(O(n\log E)\)

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define ll long long
using namespace std;
const ll N=1e5+10,M=N*40;
ll n,a[N],b[N],sum,ans,cnt,k,l,rt;
ll w[M],v[M],ls[M],rs[M];
ll read(){
	ll x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
ll print(ll x)
{if(x>9)print(x/10);putchar(x%10+'0');}
void Change(ll &x,ll L,ll R,ll pos,ll val){
	if(!x)x=++cnt;w[x]+=val;v[x]+=val*pos;
	if(L==R)return;ll mid=(L+R)>>1;
	if(pos<=mid)Change(ls[x],L,mid,pos,val);
	else Change(rs[x],mid+1,R,pos,val);
	return;
}
ll Ask(ll x,ll L,ll R,ll k){
	if(!x)return 0;
	if(L==R)return k*L;
	ll mid=(L+R)>>1;
	if(w[rs[x]]>=k)return Ask(rs[x],mid+1,R,k);
	return Ask(ls[x],L,mid,k-w[rs[x]])+v[rs[x]];
}
signed main()
{
	n=read();
	for(ll i=1;i<=n;i++)a[i]=read();
	for(ll i=1;i<=n;i++)b[i]=read();
	k=read();l=read();rt=cnt=1;
	for(ll i=n-k+1;i<=n;i++)Change(rt,1,1e9,b[i],1),sum+=a[i];
	ans=max(ans,sum+Ask(rt,1,1e9,l));
	for(ll i=1;i<=k;i++){
		Change(rt,1,1e9,b[n-k+i],-1);
		Change(rt,1,1e9,b[i],1);
		sum+=a[i];sum-=a[n-k+i];
		ans=max(ans,sum+Ask(rt,1,1e9,l));
	}
	print(ans);putchar('\n');
	return 0;
}