• [提高组互测] Day6


    总结

    这签到构造题我做不出也没有办法啊(...)

    还是感谢 ( t Oneindark) 大佬的供题,希望她以后不要再出阴间构造题啦!

    Ciel and Flipboard

    题目描述

    点此看题

    解法

    我是这样打爆搜的,枚举左上角 (m imes m) 个元素的状态,那么整个矩阵的状态是唯一确定的。因为操作矩阵个数也是 (m imes m) 个,我们可以知道它们是线性不相关的。

    下一个结论就比较难观察出来了,因为长度是 (m=frac{n+1}{2}) 很特殊,设 (s(x,y)) 表示位置 ((x,y)) 的符号位,如果是 (1) 我们设置成 (0),如果是 (-1) 我们设置成 (1),可以发现:

    [s(x,y)oplus s(x,y+m)oplus s(x,m)=0 ]

    [s(x,y)oplus s(x+m,y)oplus s(m,y)=0 ]

    对这两个式子更深的理解就是当我们确定了"中线"以后,两边的符号位就是对应的。

    ( t Rainybunny) 通过打表发现了这个结论,这种符号位问题通常存在特殊结论哦~~

    那么我们枚举 (s(m,i),ileq m),这时候 (m) 这一整行都能够确定,然后发现每一行是互相独立的,对于每一行单独做,我们先枚举中间点,然后枚举左上角的点,这时候四个点都确定了:

    那么疯狂取最大值就行了,时间复杂度 (O(2^mm^2))

    总结

    想办法把变量独立起来,然后分别去最值,你可能需要枚举一些关键变量。

    #include <cstdio>
    #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 n,m,ans,a[M][M];
    int val(int x,int y)
    {
    	return x==0?y:-y;
    }
    int check(int S)
    {
    	int o=S>>m-1,res=0;
    	for(int i=1;i<=m;i++)
    		res+=val((S>>i-1)&1,a[m][i]);
    	for(int i=m+1;i<=n;i++)
    		res+=val(o^((S>>i-m-1)&1),a[m][i]);
    	for(int i=1;i<m;i++)
    	{
    		int mx=-(1ll<<60);
    		for(int p=0;p<2;p++)
    		{
    			int s=val(p,a[i][m])+val(o^p,a[i+m][m]);
    			for(int j=1;j<m;j++)
    			{
    				int x=a[i][j]+val(p,a[i][m+j])
    				+val((S>>j-1)&1,a[m+i][j])
    				+val(((S>>j-1)&1)^o^p,a[i+m][j+m]);
    				if(x<0) x=-x;s+=x;
    			}
    			mx=max(mx,s);
    		}
    		res+=mx;
    	}
    	return res;
    }
    signed main()
    {
    	//freopen("taozi.in","r",stdin);
    	//freopen("taozi.out","w",stdout);
    	n=read();m=(n+1)/2;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			a[i][j]=read();
    	for(int i=0;i<(1<<m);i++)
    		ans=max(ans,check(i));
    	printf("%lld
    ",ans);
    }
    

    Shifting Dominoes

    题目描述

    点此看题

    解法

    考虑两个空格的独立性,首先我们可以把矩阵黑白染色,那么空格初始颜色就不同,并且每次都是把平移两格,所以颜色是不会变的,这提示黑色格和白色格可以分开处理。

    那么对于一个空格我们想求出它能到达的点,可以用图论表示这个过程,具体来说我们这样连边:

    这个边就代表了空格能够沿着边的方向转移,不难发现每个点的入度至多为 (1),所以这是可以基环外向树,你可以通过反证如果构成环内部点数为奇数来说明不会有环,所以我们得到了一个树形结构

    那么一个空格能到达的位置对应着树上的一个子树,我们可以把它表示成一个 ( t dfn) 序区间,综合考虑两维就变成了一个矩形求并问题,用扫描线解决即可,时间复杂度 (O(nmlog nm))

    总结

    多个对象的问题可以找独立性来转化成单个对象的问题。

    遇到奇怪的问题可以多想想图论,基本的分析还是要有。

    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int M = 200005;
    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,k,t,Ind,p[M][2],cl[M],rt[M],di[M],dt[M];
    vector<int> g[M];char s[M];long long ans;
    struct node
    {
    	int x,l,r,f;
    	bool operator < (const node &b) const
    	{
    		return x<b.x;
    	}
    }q[M];
    int id(int x,int y)
    {
    	return (x-1)*m+y;
    }
    void add(int x,int y,int a,int b)
    {
    	g[id(x,y)].push_back(id(a,b));
    	rt[id(a,b)]=1;
    }
    void dfs(int u)
    {
    	di[u]=++Ind;
    	for(auto v:g[u])
    		if(!di[v]) dfs(v);
    	dt[u]=Ind;
    }
    int mi[4*M],num[4*M],tag[4*M];
    void upd(int i,int c)
    {
    	tag[i]+=c;
    	mi[i]+=c;
    }
    void down(int i)
    {
    	if(!tag[i]) return ;
    	upd(i<<1,tag[i]);
    	upd(i<<1|1,tag[i]);
    	tag[i]=0;
    }
    void build(int i,int l,int r)
    {
    	num[i]=r-l+1;mi[i]=0;
    	if(l==r) return ;
    	int mid=(l+r)>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    }
    void add(int i,int l,int r,int L,int R,int f)
    {
    	if(L>r || l>R) return ;
    	if(L<=l && r<=R)
    	{
    		upd(i,f);
    		return ;
    	}
    	int mid=(l+r)>>1;down(i);
    	add(i<<1,l,mid,L,R,f);
    	add(i<<1|1,mid+1,r,L,R,f);
    	mi[i]=min(mi[i<<1],mi[i<<1|1]);num[i]=0;
    	if(mi[i]==mi[i<<1]) num[i]=num[i<<1];
    	if(mi[i]==mi[i<<1|1]) num[i]+=num[i<<1|1];
    }
    int main()
    {
    	//freopen("domino.in","r",stdin);
    	//freopen("domino.out","w",stdout);
    	n=read();m=read();
    	//build the graph
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%s",s+1);
    		for(int j=1;j<=m;j++)
    		{
    			cl[id(i,j)]=(i+j)&1;
    			if(s[j]=='L' && j+2<=m)
    				add(i,j+2,i,j);
    			if(s[j]=='R' && j-2>=1)
    				add(i,j-2,i,j);
    			if(s[j]=='U' && i+2<=n)
    				add(i+2,j,i,j);
    			if(s[j]=='D' && i-2>=1)
    				add(i-2,j,i,j);
    			if(s[j]=='L' || s[j]=='U')
    			{
    				++k;p[k][0]=id(i,j);
    				if(s[j]=='L') p[k][1]=id(i,j+1);
    				if(s[j]=='U') p[k][1]=id(i+1,j);
    			}
    		}
    	}
    	for(int i=1;i<=n*m;i++)
    		if(!rt[i]) dfs(i);
    	for(int i=1;i<=k;i++)
    	{
    		int x=p[i][0],y=p[i][1];
    		if(cl[x]) swap(x,y);
    		q[++t]=node{di[x],di[y],dt[y],1};
    		q[++t]=node{dt[x]+1,di[y],dt[y],-1};
    	}
    	//scaning line
    	sort(q+1,q+1+t);k=n*m;
    	build(1,1,k);
    	for(int i=1,j=1;i<=k;i++)
    	{
    		while(j<=t && q[j].x<=i)
    		{
    			add(1,1,k,q[j].l,q[j].r,q[j].f);
    			j++;
    		}
    		ans+=k-(mi[1]==0?num[1]:0);
    	}
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    计算器修正代码
    AsEnumrable和AsQueryable的区别
    c# 解释器模式与sping.net表达式的结合应用(金融里经常需要用到公式,这个公式是抽象的需要自己解释)
    Spring.net 间接调用被AOP拦截的方法失效(无法进入aop的拦截方法)
    信息熵公式的由来(转)
    期望风险、经验风险与结构风险之间的关系(转)
    梯度(转)
    奇特的数学问题(转)
    最大似然估计和最大后验估计(转)
    贝叶斯思想以及与最大似然估计、最大后验估计的区别(转)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15423742.html
Copyright © 2020-2023  润新知