Codeforces 871 div4(重拳出击)

发布时间 2023-05-07 10:07:03作者: Liang2003

Codeforces 871 div4

ABC

简单题

D

题意

每次操作可以将当前的数分成两份,一份是\(\frac{1}{3}\),一份是\(\frac{2}{3}\),问当前数n可否进行若干次操作,最终出现一份大小为m的片。递归一下就好了,数据最大才\(10^7\)

代码

void dfs(int x) 
{	
	if(x==m) {flag=1;return;}
	if(flag) return;//找到了就不用再递归了
	if(x%3) return;
	dfs(x/3*2);
	dfs(x/3);
}

void solve()
{	
	flag=0;
	cin>>n>>m;
	if(n<m) {cout<<"NO\n";return;}
	if(n==m){cout<<"YES\n";return;}
	if(n%3) {cout<<"NO\n";return;}
	flag=0;
	dfs(n);
	if(flag) {cout<<"YES\n";}
	else cout<<"NO\n";
}

E (FloodFill)

题意

经典dfs了

代码

int n,m,k;
int ans,tmp;
int a[1100][1100];
bool vis[1100][1100];
int flag=0;

int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int sum=0;//能遍历到的点的总和
void dfs(int x,int y) 
{
	for(int i=0;i<4;i++) 
	{
		int nx=x+dx[i],ny=y+dy[i];
		if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&a[nx][ny]&&!vis[nx][ny]) 
		{
			vis[nx][ny]=1;
			sum+=a[nx][ny];
			dfs(nx,ny);
		}
	}
}

void solve()
{	
	cin>>n>>m;
	ans=0;
	for(int i=1;i<=n;i++) 
	{
		for(int j=1;j<=m;j++) cin>>a[i][j],vis[i][j]=0;
	}
	for(int i=1;i<=n;i++) 
	{
		for(int j=1;j<=m;j++) 
		{
			if(!vis[i][j]&&a[i][j]) 
			{	
				vis[i][j]=1;
				sum=a[i][j];
				dfs(i,j);
				ans=max(ans,sum);
			}
		}
	}
	cout<<ans<<endl;
}

F

题意

题目给出一个雪花图,从根节点延伸出x个子节点,每个子节点又延伸出y个子节点

思路

先扫一遍树统计一下每个点的子节点个数,然后再扫一遍遍历就ok了。注意x和y都大于1

代码

int n,m,k;
int ans,tmp;
int h[N],e[N*2],ne[N*2],idx;
int cnt[N],cnt1[N];
int x=-1,y=-1;
void add(int a,int b) 
{
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}

void dfs(int u,int fa) 
{
	cnt[u]=1;
	for(int i=h[u];i!=-1;i=ne[i]) 
	{
		int j=e[i];
		if(j==fa) continue;
		dfs(j,u);
		cnt[u]+=cnt[j];
	}
}

void dfs1(int u,int fa) 
{		
	if(x>1) return;//找到直接返回
	int tmp=-1,flag=1;
	if(u==1) //特判根节点,因为它没有父节点,不用考虑父亲的那棵树的情况
	{
		for(int i=h[u];i!=-1;i=ne[i]) 
		{
			int j=e[i];
			if(j==fa) continue;
			if(tmp==-1) tmp=cnt[j];
			if(tmp!=cnt[j]) {flag=0;break;}
		}
	}
	else 
	{	
		tmp=n-cnt[u];//父亲那颗树的大小
		for(int i=h[u];i!=-1;i=ne[i]) 
		{
			int j=e[i];
			if(j==fa) continue;
			if(tmp!=cnt[j]) {flag=0;break;}
		}
	}

	if(flag==1) 
	{
		int c1=0;
		for(int i=h[u];i!=-1;i=ne[i]) 
		{
			c1++;
		}
		if(c1>1) x=c1,y=(n-c1-1)/x;
	}
	for(int i=h[u];i!=-1;i=ne[i]) 
	{
		int j=e[i];
		if(j==fa) continue;
		dfs1(j,u);
	}

}

void solve() 
{
	memset(h,-1,sizeof(h));
	idx=0;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cnt[i]=0,cnt1[i]=0;
	for(int i=1;i<=m;i++) 
	{
		int a,b;
		cin>>a>>b;
		add(a,b);
		add(b,a);
	}
	if(n==2) {cout<<1<<" "<<0<<endl;return;}
	dfs(1,-1);
	x=-1,y=-1;
	dfs1(1,-1);
	cout<<x<<" "<<y<<endl;
}

G

题意

一个金字塔,然后把某个块搞垮,与它上表面接触的块也会搞垮,依次类推,问搞垮某个块,垮掉的块的总贡献是多少。块n的贡献为\(n^2\)

思路

一开始想的很复杂,又分左右情况去讨论,记忆化搜索好像又不可行。后来想着暴力,过啦。
暴力思路:用个f数组记录每个数的位置。

代码

int f[2010][2010],g[2010][2010];
void init() 
{	
	int x=1;
	for(int i=1;i<=2000;i++) 
	{
		for(int j=1;j<=i;j++) 
		{
			f[i][j]=x*x;
			x++;
		}
	}
}

int x,y;

void get(int n) 
{
	x=1;
	while(n>x) n-=x,x++;
	y=n;
}

void solve() 
{
	cin>>n;
	get(n);//获取n的位置
	int ans=0;
	int k=1;
	while(x) 
	{	
		for(int i=max(0ll,y-k+1);i<=y;i++) ans+=f[x][i];
		x--,k++;
	}
	cout<<ans<<endl;
}

H(补)

题意

给了一个长度为n的数组,问有多少个不为空的子数组,它们的与的结果是只有k个1.

思路

dp。设状态\(f[i][j]\)为前i个数能构成结果j的方案。
转移方程:
\(f(i,j\&a[i])=f(i-1,j\&a[i])+f[j]+(j==a[i])\)

代码

int n,m,k;
int ans,tmp;
int a[N],f[64];

void solve() 
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) 
	{
		cin>>a[i];
	}
	for(int i=0;i<64;i++) f[i]=0;
	for(int i=1;i<=n;i++)
	{	
		for(int j=0;j<=63;j++) //j&a[i]<=j,正着来
		{	
			f[j&a[i]] = (f[j&a[i]]+f[j]%mod)%mod;
			if(j==a[i]) f[j]=(f[j]+1)%mod;
		}
	}
	int ans=0;
	for(int i=0;i<64;i++) 
	{
		int cnt=0;
		for(int j=0;j<=6;j++) if((i>>j)&1) cnt++;
		if(cnt==m) ans=(ans+f[i])%mod;
	}
	cout<<ans<<endl;
}