bzoj2728理解了好久

因为以前也没怎么碰到过NADN的题目,所以第一次碰非常陌生没有感觉。什么等价类什么东西完全懵逼。下面我尽量朴素的讲解一下我的看法。

首先NAND 可以模拟任何一种位运算。(xor,and ,not,or)这个记一下就好,好像或非也是这样。 然后显然可以知道如果a1~an z在二进制下P和Q位都相等,那么显然怎么搞这两位在ai上都还是相等的。(这个很显然)然后我们就称这些位上构成一个等价类,然后网上大都就直接说等价类是互相独立的。这话听着很正确,但是似乎也不是那么容易理解和证明,然后我就想了个比较傻逼的方法,

对于每一位,我们枚举每一个数,若该数该位上为0,我们就对这个数取非

然后把所有数取与

该位上都是1,所以取与后一定是1;对于其他位,只要有这两位不同的数存在,那么这位一定是0

最后取与的结果中与该位全部相同的位都是1,其余都是0,、

这样好像就把各个等价类成功分离了。然后我们大概就可以说他们是互相独立的了。其实这也就是把他们分离出来的方法也就相当于线性基,然后做一下昨天用的位数dp就好了,这里注意一下,这个 题的线性基是没有任何一位会重复的,所以对于每个线性基能造成的影响不过让这些位变成0或1两种,所以用or或xor运算是一样的

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define eps 1e-8
#define maxn 510
#define Abs(x) ((x) > 0 ? (x) : (-(x)))  
#define forup(i,a,b) for(int i=(a);i<=(b);i++)  
#define fordown(i,a,b) for(int i=(a);i>=(b);i--)  
using namespace std;

struct yts
{
 long double b[maxn];
	int val;
}a[maxn];

int n,ans,m,num;
int vis[maxn];

bool cmp(yts x,yts y)
{
	return x.val<y.val;
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	  for (int j=1;j<=m;j++)
	 cin>>a[i].b[j];
	for (int i=1;i<=n;i++) scanf("%d",&a[i].val);
	sort(a+1,a+n+1,cmp);
/*for (int i=1;i<=n;i++)
	  for (int j=1;j<=m;j++)
	    if (fabs(a[i].b[j])>eps)
	    {
	    	if (!vis[j])
	    	{
	    		vis[j]=i;
	    		ans+=a[i].val;num++;
	    		break;
	    	}
	    	else
	    	{
	    		long double t=(long double)a[i].b[j]/(long double)a[vis[j]].b[j];
	    		for (int k=j;k<=m;k++)
	    		  a[i].b[k]-=t*a[vis[j]].b[k];
	    	}
	    }*/
	    forup(i,1,m)
	    {int b=0;
	    forup(j,1,n)
	      { if(fabs(a[j].b[i])>eps&&!vis[j])
	         { b=j;vis[j]=1;
	           break;
			 }
		  }
		  if(b!=0) {num++;ans=ans+a[b].val;}
		   else    continue;
		   forup(j,1,n)
		    {if(j!=b&&fabs(a[j].b[i])>eps)
		      { long double tmp=(long double)a[j].b[i]/(long double)a[b].b[i];
		           forup(k,1,m)
		           a[j].b[k]=a[j].b[k]-tmp*a[b].b[k];
			  }
			   
			}
		}
	printf("%d %d\n",num,ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值