BZOJ2728 HNOI2012与非(并查集+数位dp)

  容易发现x nand x=not x。并且使用这个性质有x and y=not(x nand y)=(x nand y)nand(x nand y)。也就是说nand运算可以作为not和and运算使用。并且显然not和and运算可以表示nand运算,那么两者等价。事实上这就可以表示所有位运算了。

  那么考虑位运算有什么事干不了。注意到如果每个数的第i位都和第j位相同, 那么无论怎么操作这两位都是相同的。大胆猜想这也是充分的,即除了这件事其他都能干。

  这样位就被分成了很多类,每一类的取值要求相同。类似数位dp搞一发考虑第一个未达限制的是哪一位就行了。

  各种细节,调到吐血,拿过7种分数,连过了都不知道是不是数据水了,没救。

#include 
#include
#include
#include
#include
#include
using namespace std;
#define N 1010
#define K 60
#define ll long long
ll read()
{
    ll x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,k,d[K][K],fa[K];
bool flag[K];
ll l,r,num[K];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
ll calc(ll n)
{
    ll s=0,m=0,cnt=0;
    for (int i=0;iif (find(i)==i) cnt++;
    memset(flag,0,sizeof(flag));
    for (int i=k-1;~i;i--)
    {
        if (find(i)==i) cnt--;
        if ((n&(1ll<flag[find(i)]) 
        {
            s+=1ll<<cnt;
            flag[find(i)]=1;
            if ((m|=num[find(i)])>n) break;
        }
    }
    for (int i=0;i)
        for (int j=0;j)
        if (find(i)==find(j)&&((n&(1ll<0)!=((n&(1ll<0)) return s;
    return s+1;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj2728.in","r",stdin);
    freopen("bzoj2728.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),k=read(),l=read(),r=read();
    if (l>=(1ll<0;return 0;}
    r=min(r,(1ll<1);
    for (int i=0;i)
        for (int j=0;j)
        d[i][j]=1;
    for (int i=1;i<=n;i++)
    {
        ll x=read();
        for (int p=0;p)
            for (int q=0;q)
            if (((x&(1ll<0)!=((x&(1ll<0)) d[p][q]=0;
    }
    for (int i=0;ii;
    for (int i=0;i)
        for (int j=0;j)
        if (d[i][j]) fa[find(i)]=find(j);
    for (int i=0;i)
    num[find(i)]|=1ll<<i;
    cout<1):0);
    return 0;
}

 

转载于:https://www.cnblogs.com/Gloid/p/9600287.html

你可能感兴趣的:(BZOJ2728 HNOI2012与非(并查集+数位dp))