2021.08.29 膜你赛

2021.08.29 膜你赛

divide

Description

问是否可以将一个仅由 0~9 组成的字符串划分成两个或两个以上部分,使得

每一部分的数字总和相等。

Solution

当时就是想不出 \(O(n)\) 的方法来,然后就只想到了 \(n^2\) 的方法。尽管加了优化,但还是很慢,\(O(n)\) 的真的很好写!!!

直接枚举答案要划分的区间数,如果如果总和不能整除区间数,说明不合法,如果可以,从头开始计算是否可以划分成当前枚举的区间数,枚举从小到大,符合条件直接返回。

Code

/*
* @Author: smyslenny
* @Date:    2021.08.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define int long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int mod=998244353;
const int M=1e5+5;
int n;
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
char s[M];
int num[M],sum[M];
int len,js;
void init()
{

	len=strlen(s+1),js=0;
	for(int i=1;i<=len;i++) 
		if(s[i]-'0'!=0) 
			num[++js]=s[i]-'0',sum[js]=sum[js-1]+num[js];	
}

signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		init();
		int fg=0;
		if(sum[js]==0) 
		{
			printf("YES\n");
			continue;
		}
		for(int i=2;i<=sum[js];i++)
		{
			if(sum[js]%i) continue;
			int duan=sum[js]/i,cnt=0,las=0;
			for(int j=1;j<=js;j++)
			{
				if(sum[j]-sum[las]==duan) cnt++,las=j;
				else if(sum[j]-sum[las]>duan) break;
			}
			if(cnt==i)
			{
				fg=1;
				break;
			}
		}
		if(fg==1) 
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}
/*
92116543789654796748307452898565687837416361989474778740
*/

Count

Description

求有多少个由 n 个带编号的点组成的无向图是连通的

Solution

考试的时候一直在想推公式求答案,但是方案太多了,然后公式就炸掉了。

一种方法就是用总的连边数量减去不合法的方案数量,设 \(f_i\)​ 表示 \(i\)​ 个编号的连通图的个数,总的连边的方案数为 \(2^{C_n^2}\)​ ,不合法的为 \(\sum\limits_{j=1}^{i-1}f_i\times C_{i-1}^{j-1}\times 2^{i-j}\)​ ,所以答案为 \(f_i=2^{\frac{n\times (n-1)}{2}}-\sum\limits_{j=1}^{i-1}C_{i-1}^{j-1}\times f_j\times 2^{i-j}\)​

/*
* @Author: smyslenny
* @Date:    2021.08.
* @Title:   
* @Main idea:用合法的方案数减去不合法的方案数,总方案数是 2^{n*(n-1)/2} 
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define int long long
#define INF 0x3f3f3f3f
#define inv 499122177
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int mod=998244353;
const int M=5e3+5;
int n,Ans[M],c[M][M];
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
int qpow(int a,int b)
{
	int res=1; 
	while(b)
	{
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
			
int f[M],g[M];//合法的方案数,不合法的方案数 
signed main()
{
//	freopen("count.in","r",stdin);
//	freopen("count.out","w",stdout); 
	n=read();
	for(int i=0;i<=n;i++)
		g[i]=qpow(2,i*(i-1)/2);
	g[0]=1,c[0][0]=1;
	for(int i=0;i<=n;i++)
		for(int j=0;j<=i;j++)
		{
			if(c[i][j]>mod) c[i][j]-=mod;
			c[i+1][j+1]+=c[i][j];
			c[i+1][j]+=c[i][j];	
		}
	f[0]=1;
	for(int i=1;i<=n;i++)
	{
		int cnt=0;
		for(int j=1;j<i;j++)
			cnt=(cnt+c[i-1][j-1]*f[j]%mod*g[i-j]%mod)%mod;
		f[i]=g[i]-cnt;
		if(f[i]<0) f[i]+=mod;	
	}
	printf("%lld\n",f[n]);
	return 0;
}

max

Description

有n个物品,每个物品有两个属性a[i],b[i]

​ 挑选出若干物品,使得这些物品a[i]的异或和<=m。问在这一限制下,b[i]的总和最大可能为多少。

Code

/*
* @Author: smyslenny
* @Date:    2021.08.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int mod=998244353;
const int M=1e3+5;
int n,mp[M],m,Ans,now;
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}

struct ed{
	int u,v,w,net;
}edge[M<<1];
struct st{
	int u,v,sum,lca;
}sz[M];
int head[M],num;
void addedge(int u,int v)
{
	edge[++num].u=u;
	edge[num].v=v;
	edge[num].net=head[u];
	head[u]=num;
}
int f[M],siz[M],dep[M],son[M],sum[M],dfn[M],js;

void dfs(int u,int fa)
{
	sum[u]+=mp[u];
	dep[u]=dep[fa]+1;
	for(int i=head[u];i;i=edge[i].net)
	{
		int v=edge[i].v;
		if(v==fa) continue;
		f[v]=u;
		sum[v]+=sum[u];
		dfs(v,u);
	}
	dfn[u]=++js;
}

int top[M];
void dfs_2(int x,int tp)
{
	top[x]=tp;
	if(!son[x]) return;
	dfs_2(son[x],tp);
	for(int i=head[x];i;i=edge[i].net)
	{
		int v=edge[i].v;
		if(v!=f[x] && v!=son[x])
			dfs_2(v,v);
	}
}

int lca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    while(dep[x]!=dep[y]) x=f[x];
    if(x!=y) x=f[x],y=f[y];
    return x;
}
int g[M];
void solve(int x)
{
	int u=sz[x].u;
	int Ans=0,las=0;
	while(u!=sz[x].lca)
	{
		Ans+=g[u]-g[las];
		las=u;
		u=f[u];
	}
	Ans-=f[las];
	u=sz[x].v;
	las=0;
	while(u!=sz[x].lca)
	{
		Ans+=g[u]-f[las];
		las=u;
		u=f[u];
	}
	Ans-=f[las];
	Ans+=g[sz[x].lca];
	f[sz[x].lca]=max(f[sz[x].lca],Ans+sz[x].sum);
}
void dfs(int u)
{
	for(int i=head[u];i;i=edge[i].net)
	{
		int v=edge[i].v;
		if(v==f[u]) continue;
		dfs(v);
		g[u]+=f[v];
	}
	f[u]=g[u];
	while(now<=m && sz[u].lca==u) 
		solve(now++);
}

bool cmp(st a,st b)
{
	return dfn[a.lca]<dfn[b.lca];
}
 
int main()
{
//	freopen("max.in","r",stdin);
//	freopen("max.out","w",stdout); 
	n=read();
	for(int i=1;i<=n;i++) mp[i]=read();
	for(int i=1,u,v;i<n;i++)
		u=read(),v=read(),addedge(u,v),addedge(v,u);
	dfs(1,0);
	m=read();
	for(int i=1;i<=m;i++)
	{
		sz[i].u=read(),sz[i].v=read();
		sz[i].lca=lca(sz[i].u,sz[i].v);
		sz[i].sum=sum[sz[i].u]+sum[sz[i].v]-sum[sz[i].lca]*2+sum[f[sz[i].ca]];
	}
	sort(sz+1,sz+1+m,cmp);
	now=1;
	dfs_2(1); 
	printf("%lld\n",f[1]);
	return 0;
}

xor

有n个物品,每个物品有两个属性a[i],b[i]

​ 挑选出若干物品,使得这些物品a[i]的异或和<=m。问在这一限制下,b[i]的总和最大可能为多少。

Solution

考试的时候忘开 long long 直接抱零,直接暴力搜索,写一个正确性剪枝就能过了。= =

/*
* @Author: smyslenny
* @Date:    2021.08.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define int long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int mod=998244353;
const int M=1e3+5;
int n,m,f[M],Sum[M];
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
int a[M],b[M],Max,cnt,Ans;
void dfs(int x,int sum)
{
	if(sum<=m) Ans=max(Ans,cnt);
	if(x>n) return;
	if(cnt+Sum[x]<Ans) return;
	for(int i=x;i<=n;i++)
	{
		sum^=a[i];
		cnt+=b[i];
		dfs(i+1,sum);
		sum^=a[i];
		cnt-=b[i];
		dfs(i+1,sum);
		return;
	}
	return; 
}
			
signed main()
{
//	freopen("xor.in","r",stdin);
//	freopen("xor.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		a[i]=read(),b[i]=read();
	for(int i=n;i>=1;i--)
		Sum[i]=Sum[i+1]+b[i];
	dfs(1,0);
	printf("%lld\n",Ans);
	return 0;
}
/*
3 5
5 10
4 11
7 12
20 285147
384566015 438053
1586369 69409
742968588 264467
356006410 370489
232773204 506587
40294785 486731
630144330 5987
654644042 197390
100016976 279406
37854189 337579
448324032 722364
46090941 694168
17593358 888034
354401103 514925
827143807 239853
367870914 105602
457533595 439836
280088642 770991
747103100 78
587000606 652347


20 760535
99499442 502952
353382646 455794
321391841 402059
474311542 399680
879529853 630004
486135480 802906
546496667 611505
196553240 817278
889619477 336455
70866199 55712
243055187 419959
331754336 32231
20254869 999730
162511020 110757
89977380 270601
206650662 557770
222414613 410725
62426199 424101
563896289 740595
889985847 953620



*/
上一篇:CF Contest 348 Solution


下一篇:2021-09-09