• [省选集训2022] 模拟赛12


    高维游走

    题目描述

    考虑以下在 \(m\) 维空间的游走过程:初始时你在原点,即每一维坐标为 \(0\) 的位置。接下来依次有 \(\sum_{i=0}^m t_i\) 次操作,分为 \(m+1\) 个阶段。第 \(0\) 个阶段有 \(t_0\) 次操作,每次操作可以不动或者选择任意一维向其正方向前进 \(1\) 个单位长度。第 \(i(1\leq i\leq m)\) 个阶段有 \(t_i\) 次操作,每次操作可以不动或者提升 \(i\) 点疲倦度的同时向第 \(i\) 维负方向前进 \(1\) 个单位长度。

    \(f(x)\) 表示所有操作结束后回到游走起点的方案中疲劳度为 \(x\) 的方案数,定义两个方案不同当且仅当某一次操作的决策不同。你需要求 \(\sum_{i=0}^{\infty}\{f_i\bmod2\}\) 的值。

    多组数据:\(1\leq T\leq 200\)\(1\leq m\leq 10,1\leq t_i<2^{31}\)

    解法

    \(x_i\) 表示最终向第 \(i\) 维走了 \(x_i\) 步,那么它对应的方案数是:

    \[\prod_{i=1}^m {t_i\choose x_i}\cdot {t_0\choose x_1,x_2...x_m,t_0-\sum x} \]

    那么贡献为 \(1\) 的充要条件是:\(t_i\and x_i=x_i\)\(\{x\}\)\(t_0\) 二进制位的拆分。后面的性质可以用归纳法证明,首先要满足 \(t_0\and x_1=x_1\),那么 \(x_1\) 相当于把 \(t_0\) 的二进制位拆了一些出来,\(x_2\) 继续拆分 \(t_0-x_1\),那么就可以证明了。

    那么我们考虑规划合法的 \(\{x\}\),不同的方案以疲劳度来区分。考虑疲劳度是 \(\sum_{i=1}^m x_i\cdot i\),那么我们依次考虑 \(t_0\) 的每个二进制位,再决策这一位分配给 \(x\)\(0\sim m\) 的哪一个即可(如果分配 \(0\) 代表这一位不选),对于以前的位都相同的方案,我们在第一个导致数位不同的地方它们区分出来

    \(dp[i][s]\) 表示考虑前 \(i\) 位,进位到 \(2^{i+1}\) 的集合是 \(s\)(如果 \(s_j=1\) 代表进位得到 \(j\cdot 2^{i+1}\) 是可行的)的方案数,那么转移考虑枚举 \(t_0\)\(i\) 位要分配给 \(0\sim m\) 中的哪一个。注意两种方案如果此后进位还是相同那么需要抵消,所以我们可以维护两个集合 \(s_1,s_2\),表示第 \(i\) 位疲劳值是 \(1/0\) 转移到的状态,用异或的方式来抵消即可。

    时间复杂度 \(O(T\cdot 2^{m}\cdot m^2\cdot \log t)\)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int M = 50;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,m,t[M],vis[M],mp[M][M],dp[M][1<<10];
    void work()
    {
    	memset(vis,0,sizeof vis);
    	memset(mp,0,sizeof mp);
    	memset(dp,0,sizeof dp);
    	m=read();
    	for(int i=0;i<=m;i++) t[i]=read();
    	for(int i=1;i<=m;i++)
    	{
    		n=0;
    		while(t[i]) mp[n++][i]=t[i]&1,t[i]>>=1;
    	}
    	n=0;while(t[0]) vis[n++]=t[0]&1,t[0]>>=1;
    	for(int i=0;i<n;i++) mp[i][0]=1;
    	int w=0,ans=0,lim=(1<<10);
    	dp[0][1]=1;
    	for(int i=0;i<n;i++)
    	{
    		w^=1;memset(dp[w],0,sizeof dp[w]);
    		int up=vis[i]?m:0;
    		for(int j=0;j<lim;j++)
    		{
    			int s1=0,s2=0;
    			for(int k=0;k<10;k++) if(j>>k&1)
    				for(int l=0;l<=up;l++) if(mp[i][l])
    				{
    					if(k+l&1) s1^=(1<<(k+l>>1));
    					else s2^=(1<<(k+l>>1));
    				}
    			dp[w][s1]+=dp[w^1][j];
    			dp[w][s2]+=dp[w^1][j];
    		}
    	}
    	for(int i=0;i<lim;i++)
    		for(int j=0;j<10;j++)
    			if(i>>j&1) ans+=dp[w][i];
    	printf("%lld\n",ans);
    }
    signed main()
    {
    	freopen("travel.in","r",stdin);
    	freopen("travel.out","w",stdout);
    	T=read();
    	while(T--) work();
    }
    

    过山车

    题目描述

    \(1\leq n\leq 150,1\leq m\leq 30,0\leq w[i][j]\leq 100\)

    解法

    考虑最后答案的形式:若干个哈密顿圈,转角的点会产生贡献。

    显然哈密顿圈是 \(\tt np\) 问题,但因为这是平面图,所以关键性质是它可以黑白染色

    那么我们把平面图黑白染色,尝试转化为二分图问题。首先考虑如何判定,可以给每个需要覆盖的点(下文称之为关键点) \(2\) 的流量,如果两个关键点相邻那么连一条流量为 \(1\) 的边,对它跑网络流,满流是有解的充要条件。

    那么我们考虑魔改上面的图来完成最大化权值的目的,注意到弯道是竖直方向和水平方向的拼接,那么我们可以考虑拆点,对于每个 \(x\) 我们再新建 \(x_1,x_2\) 表示竖直点和水平点,那么我们在同时选取竖直点和水平点的时候计算贡献:

    上图展示了建图的一部分,另一部分是:如果 \(x,y\) 竖直相连,那么把 \(x_1,y_1\) 连一条流量为 \(1\) 的边;如果 \(x,y\) 水平相连,那么把 \(x_2,y_2\) 连一条流量为 \(1\) 的边。然后我们对这个图跑最小费用流,再用总权值去减就可以得到答案。因为如果同时使用 \(x_1,x_2\) 得到的权值是 \(0\) 最后的贡献就是 \(w\),如果只使用一边那么最后的贡献是 \(0\)

    好像这题只有 \(\tt spfa\) 才能通过 \(...\)

    #include <cstdio>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int M = 20005;
    const int inf = 0x3f3f3f3f;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,a[155][35],b[155][35],id[155][35][3];
    int tot,S,T,ans,f[M],dis[M],flow[M],pre[M],lst[M];
    int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
    struct edge{int v,f,c,next;}e[M*100];
    void add(int u,int v,int F,int c)
    {
    	e[++tot]=edge{v,F,c,f[u]},f[u]=tot;
    	e[++tot]=edge{u,0,-c,f[v]},f[v]=tot;
    }
    int bfs()
    {
    	queue<int> q;
    	for(int i=S;i<=T;i++)
    		dis[i]=inf,flow[i]=pre[i]=lst[i]=0;
    	dis[S]=0;flow[S]=inf;q.push(S);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		for(int i=f[u];i;i=e[i].next)
    		{
    			int v=e[i].v,c=e[i].c;
    			if(dis[v]>dis[u]+c && e[i].f>0)
    			{
    				dis[v]=dis[u]+c;
    				flow[v]=min(flow[u],e[i].f);
    				pre[v]=u;lst[v]=i;
    				q.push(v);
    			}
    		}
    	}
    	return flow[T]>0;
    }
    signed main()
    {
    	freopen("roller.in","r",stdin);
    	freopen("roller.out","w",stdout);
    	n=read();m=read();
    	//initialize
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    		{
    			a[i][j]=read();
    			if(!a[i][j]) for(int k=0;k<3;k++)
    				id[i][j][k]=++T;
    		}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			b[i][j]=read();
    	int A=0,B=0,sum=0;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    		{
    			if(i+j&1) A+=(!a[i][j]);
    			else B+=(!a[i][j]);
    		}
    	if(A!=B) {puts("-1");return 0;}
    	S=0;T++;tot=1;
    	//build the graph
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++) if(i+j&1)
    			for(int k=0;k<4;k++)
    			{
    				int x=i+dx[k],y=j+dy[k];
    				if(x<1 || y<1 || x>n || y>m) continue;
    				if(a[i][j]+a[x][y]) continue;
    				add(id[i][j][k%2],id[x][y][k%2],1,0);
    			}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++) if(!a[i][j])
    		{
    			int x=id[i][j][2],w=b[i][j];ans+=w;
    			if(i+j&1)
    			{
    				sum+=2;
    				add(S,x,2,0);
    				add(x,id[i][j][0],1,0);
    				add(x,id[i][j][0],1,w);
    				add(x,id[i][j][1],1,0);
    				add(x,id[i][j][1],1,w);
    			}
    			else
    			{
    				add(x,T,2,0);
    				add(id[i][j][0],x,1,0);
    				add(id[i][j][0],x,1,w);
    				add(id[i][j][1],x,1,0);
    				add(id[i][j][1],x,1,w);
    			}
    		}
    	while(bfs())
    	{
    		sum-=flow[T];
    		ans-=flow[T]*dis[T];
    		int u=T;
    		while(u!=S)
    		{
    			e[lst[u]].f-=flow[T];
    			e[lst[u]^1].f+=flow[T];
    			u=pre[u];
    		}
    	}
    	if(sum) {puts("-1");return 0;}
    	printf("%lld\n",ans);
    }
    

    木棍

    题目描述

    \(n\) 根木棍,所有木棍的长度都为 \(k\),现在要把木棍排列到数轴上,第 \(i\) 个木棍的左端点 \(\geq a_i\),右端点 \(\leq b_i\),并且要求任意两个木棍之间不能有公共长度(但是可以点交)

    此外还有 \(m\) 个限制,第 \(i\) 条形如: 左端点大于等于 \(x_i\),右端点小于等于 \(y_i\) 的木棍至多有 \(c_i\) 条。问是否存在解,注意只需要回答存不存在,而不需要构造解。

    \(n\leq 1000,m\leq 1000,k\leq 10^9\)

    解法

    首先把限制都转成左端点的限制,只需要把右端点减去 \(k\) 即可。

    如果不考虑 \(m\) 个限制并且 \(k=1\),那么剩下的限制就是每个木棍的可放置范围是一个区间,那么显然可以木棍到点连出一个二分图,然后跑网络流判断是否有解。当然可扩展的方式是使用 \(\tt Hall\) 定理,我们可以把所有端点离散化,那么只保留区间的限制就是充分的

    限制形如这段区间的木棍数量 \(\geq x\),可以用差分约束来表示限制,设 \(s_i\) 表示前缀 \(i\) 放置的木棍个数,那么 \(s_r-s_{l-1}\geq x\)

    发现 \(k\not=1\) 也是可以考虑的,也就是 \(s_{i+k}-s_i\leq 1\),那么离散化后就是 \(s_{a_i}-s_{a_{i-1}}\leq\lceil\frac{a_i-a_{i-1}}{k}\rceil\)\(m\) 的限制同样是可以考虑的,就是 \(s_{y_i}-s_{x_i-1}\leq c_i\);最后由于合法还需要添加一类边:就是 \(s_i-s_{i-1}\geq 0\)

    然后暴力 \(\tt spfa\) 即可(如果时间用完了直接无解),注意离散化时我们要把左端点 \(-1\) 丢进去。

  • 相关阅读:
    CSRF和XSS的区别
    xss攻击与防范
    GAN基础
    (转载)深度学习数据集
    Python问题解决记录
    Spark Mllib源码分析
    Spark MLlib框架详解
    Spark Structured Streaming框架(5)之进程管理
    Spark Structured Streaming框架(4)之窗口管理详解
    Spark Structured Streaming框架(3)之数据输出源详解
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15947433.html
Copyright © 2020-2023  润新知