2019CSP模拟赛题解Adore Confess Repulsed

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20180602_csq/article/details/102758631

题解

第一次在考场上A掉状压DP。。。

由于答案要求路径条数为偶数的方案数,所以我们对于每个点只需要存下它对应路径条数的奇偶性

然后只有10个点,就可以状压DP

f[i][S]表示到第i层DAG时每个点的路径条数奇偶性为S

然后我们可以枚举当前层到上一层的边状态,直接转移到f[i-1][nS]

O(m*2^k*k^2)

我们可以预处理一下每一个邻接矩阵的行和列的二进制值

O(m*2^k*k)

就可以1e8复杂度卡过了

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 10005
const int mod=998244353;
int f[N][1035],a[N][12][12],b[12],c[12],hang[N][12],lie[N][12];
int main()
{
	//freopen("1.in","r",stdin);
	freopen("adore.in","r",stdin);
	freopen("adore.out","w",stdout);
	int n,m,i,j,k;
	n=gi();m=gi();n-=2;
	for(i=0;i<m;i++)
		b[i]=gi();;
	for(i=2;i<=n;i++)
		for(j=0;j<m;j++)
			for(k=0;k<m;k++){
				a[i][j][k]=gi();
				hang[i][j]+=(a[i][j][k]<<k);
				lie[i][k]+=(a[i][j][k]<<j);
			}
	int s=0,st=(1<<m),ns1,ns2,cnt;
	for(i=0;i<m;i++){
		c[i]=gi();
		s+=(c[i]<<i);
	}
	f[n][s]=1;
	for(i=n;i>1;i--){
		for(s=0;s<st;s++){
			if(f[i][s]){
				ns1=0;ns2=0;
				for(j=0;j<m;j++)
					if((s>>j)&1){
						ns1^=lie[i][j];
						ns2^=hang[i][j];
					}
				f[i-1][ns1]+=f[i][s];
				if(f[i-1][ns1]>=mod)f[i-1][ns1]-=mod;
				f[i-1][ns2]+=f[i][s];
				if(f[i-1][ns2]>=mod)f[i-1][ns2]-=mod;
			}
		}
	}
	int ans=0;
	for(s=0;s<st;s++){
		if(f[1][s]){
			cnt=0;
			for(j=0;j<m;j++)
				if(s&(b[j]<<j))
					cnt++;
			if(!(cnt&1)){
				ans+=f[1][s];
				if(ans>=mod)ans-=mod;
			}
		}
	}
	printf("%d",ans);
}

题解

有一个利用bitset的O(n^3/64)的显然做法(其实预处理已经n^2了,为什么不T呢……)

然后我们可以证明随机猜中答案的期望次数是3n

所以我们只要知道一个解的时候就可以return 0了

结果考试的时候pw只开了10,然后爆零了。。。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;
#define ULL unsigned long long
#define N 6005
char s[2005];
int pw[100];//XXXXXXXXXXXXXXXXXXXXXXXXX
bitset<12015>a[N];
int main()
{
	freopen("confess.in","r",stdin);
	freopen("confess.out","w",stdout);
	int n,i,j,len=0,tmp;
	scanf("%d",&n);n++;
	pw[1]=0;pw[2]=1;pw[4]=2;pw[8]=3;pw[16]=4;pw[32]=5;pw[64]=6;
	for(i=1;i<=n;i++){
		scanf("%s",s+1);
		if(!len){
			len=strlen(s+1);
			tmp=6*len-2*n;
			tmp=(1<<6)-(1<<tmp);
		}
		s[len]=char(((s[len]-33)&tmp)+33);
		int k=0;
		for(j=1;j<=len;j++){
			int x=s[j]-33;
			while(x){
				a[i].set(k+pw[x&-x]);
				x-=x&-x;
			}
			k+=6;
		}
	}
	for(i=1;i<n;i++){
		for(j=i+1;j<=n;j++){
			if(int((a[i]&a[j]).count())>=(n>>1|1)){
				printf("%d %d",i,j);
				return 0;
			}
		}
	}
	printf("NO Solution");
}

题解

明显贪心

然后开始用左偏树乱做。。。WA爆了。。。

想一想,其实这道题并不难

贪心的一个思路就是在恰好满足自身条件的情况下,尽可能大的减少其他地方的代价

也就是将自己子树中的距离恰好为k的点覆盖完后,尽可能地去覆盖其他较深的点

于是我们设f[i][k]表示i的子树中与i距离为k的未覆盖的点的个数

g[i][k]表示i的子树中与i距离为k的可用的灭火器的剩余灭火次数

由于K<=20,我们就可以直接枚举k的大小,用g[i][k]来覆盖f[i][K-k]和f[i][K-k-1](因为K-k-1再向上走就无法与g[i][k]距离<=k了)

最后要在1号点把所有的未覆盖点再像之前一样覆盖一遍,统计最后的未覆盖点数,再确定1号店放多少灭火器

就做完啦

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 300005
int n,s,k;
int fir[N],to[N],nxt[N],fa[N],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int f[N][25],g[N][25],ans;
void dfs(int u)
{
	int v,p,i,tmp;f[u][0]=1;
	for(p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v!=fa[u]){
			fa[v]=u;dfs(v);
			for(i=1;i<=k;i++){
				f[u][i]+=f[v][i-1];
				g[u][i]+=g[v][i-1];
				g[u][i]=min(g[u][i],n);
			}
		}
	}
	if(f[u][k]){
		ans+=int(1.0*f[u][k]/s+0.9999999);
		g[u][0]+=min(n,int(1.0*f[u][k]/s+0.9999999)*s);
	}
	for(i=0;i<=k;i++){
		tmp=min(f[u][i],g[u][k-i]);
		f[u][i]-=tmp;g[u][k-i]-=tmp;
	}
	for(i=0;i<k;i++){
		tmp=min(f[u][i],g[u][k-i-1]);
		f[u][i]-=tmp;g[u][k-i-1]-=tmp;
	}
}
int main()
{
	freopen("repulsed.in","r",stdin);
	freopen("repulsed.out","w",stdout);
	int i,j,u,v,tmp;
	n=gi();s=gi();k=gi();
	for(i=1;i<n;i++){
		u=gi();v=gi();
		adde(u,v);
	}
	dfs(1);
	for(i=0;i<=k;i++)
		for(j=k;j>=0;j--){
			if(i+j<=k){
				tmp=min(f[1][i],g[1][j]);
				f[1][i]-=tmp;g[1][j]-=tmp;
			}
		}
	int sum=0;
	for(i=0;i<=k;i++)
		sum+=f[1][i];
	printf("%d",ans+int(1.0*sum/s+0.9999999));
}

猜你喜欢

转载自blog.csdn.net/C20180602_csq/article/details/102758631