bzoj 2728 与非 【找规律】 【位运算】 【数位dp】

通过与非运算可以组合出and和not,然后and和not可以组合所有的逻辑运算。

所以只要有可能,没有表示不出来的数。但是如果所有数都满足st[i]=st[j] 那么这两位无论如何都相等。

并查集维护所有相等的类别,数位dp

还是不太熟练。

01数位dp的本质是枚举所有的非0位,假设它是0,更新答案,然后把它设为1,求之后的答案。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>

#define ll long long
#define inf 1e9
#define eps 1e-10
#define md
#define N 100010
using namespace std;
int mark[N],fa[N];
ll a[N];
bool vis[N];
int K;
int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
int getnum(int x)
{
	memset(vis,0,sizeof(vis));
	for (int i=x;i>=0;i--) vis[find(i)]=1;
	//for (int i=0;i<=x;i++) printf("%d %d  ",fa[i],vis[i]); 
	int ans=0;
	for (int i=0;i<=x;i++) ans+=vis[i];
	return ans;
}
	
ll dp(ll x)
{
	if (x<0) return 0;
	memset(mark,-1,sizeof(mark));
	ll ans=0; int t=getnum(K-1); //printf("t %d\n",t);
	if ((x>>K)) return 1ll<<t;
	for (int k=K-1;k>=0;k--)
	{
		int f=find(k);
		if ((x>>k)&1)
		{
			if (mark[f]==1) continue;
			if (mark[f]==-1)
			{
				mark[f]=1; t--;
				ans+=(1ll<<t);
			}
			else if (mark[f]==0)
			{
				ans+=(1ll<<t);
				return ans;
			}
			if ((!k)&&mark[f]==1) ans++;
		}
		else
		{
			if (mark[f]==-1)
			{
				mark[f]=0; t--;
			}
			else if (mark[f]==1) return ans;
			if ((!k)&&mark[f]==0) ans++;
		}
	}
	return ans;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("data.in","r",stdin); //freopen("data.out","w",stdout);
#endif
	int n; ll l,r;
	scanf("%d%d%lld%lld",&n,&K,&l,&r);
	for (int i=1;i<=n;i++)
	  scanf("%lld",&a[i]);
	for (int i=0;i<K;i++) fa[i]=i;
	for (int i=0;i<K;i++)
	  for (int j=i+1;j<K;j++)
	  {
	  	bool bo=1;
	  	for (int k=1;k<=n;k++)
	  	  if (((a[k]>>i)&1)!=((a[k]>>j)&1))
	  	  {
	  	  	bo=0;
	  	  	break;
	  	  }
	  	if (bo)
	  	{
	  		int f1=find(i),f2=find(j);
	  		if (f1!=f2) fa[f1]=f2;
	  	}
	  }
	//printf("%lld %lld\n",dp(r),dp(l-1));
	printf("%lld\n",dp(r)-dp(l-1));
	return 0;
}
	

你可能感兴趣的:(位运算,dp)