zoukankan      html  css  js  c++  java
  • CTSC2017密钥、吉夫特

    自己是有多么sb。

    密钥

    大家都说这是一道普及-的题,一年前我做不起,我可以说我太弱啦,我就普及组水平,今年我还是做不起……

    看大佬题解都是:开个桶就好啦!

    我:你在说什么……

    首先把环拉成链,倍长。

    如果确定$i$这个位置是起始位置,那么特征值就是$sumlimits_{j=1}^{n-1} (p_j!=0 , sum(A_{i+1}...A_{i+j})>0) $。

    那么我们先记录一个前缀和,后面所提到的$A$都是前缀和。$sumlimits_{j=1}^{n-1} (p_j!=0 , A_{i+j} > A_{i})$。

    我们可以这样来考虑,我们把$A$看作高度,从左到右连起来看成折线,我每次在$A_{i}$高度处拉根线,比线高的,并且向上走的(就是p!=0),的折线的个数就是特征值

    因为$A_i-A_{i-1}$要么是1要么是-1,所以我们每次减掉或者加上某一个取值的个数就可以了,所以这就是开个桶的意义。

    然后如果$p!=0$是B的位置的时候,如果图不变,相当于比线低的,往下走的,的折线的个数,就是特征值,

    然后因为折线起始点和终止点的高度都是拉的这根线的高度,

    所以比线低的,往下走的,和比线低或者相等的,往上走的,是可以一一对应的,数目是相等的,

    比线低或者相等的,往上走的个数,就是k-比线高的,并且向上走的个数

    我在说啥……

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=4e7+7;
    int n,k,seed,now,S,p[maxn],cnt[maxn],A[maxn],ans1,ans2,ans3;
    
    char cc; ll ff;
    template<typename T>void read(T& aa) {
    	aa=0;cc=getchar();ff=1;
    	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    int getrand(){
        seed = ((seed * 12321) ^ 9999) % 32768;
        return seed;
    }
    
    void generateData(){
        scanf("%d%d%d",&k,&seed,&S);
        int t = 0;
        n = k * 2 + 1;
        memset(p, 0, sizeof(p));
        for (int i = 1; i <= n; i++)
        {
            p[i] = (getrand() / 128) % 2;
            t += p[i];
        }
        int i = 1;
        while (t > k)
        {
            while (p[i] == 0)
                i++;
            p[i] = 0;
            t--;
        }
        while (t < k)
        {
            while (p[i] == 1)
                i++;
            p[i] = 1;
            t++;
        }
    }
    
    int main() {
    	generateData();
    	For(i,1,n) p[i+n]=p[i];
    	A[0]=n+1; For(i,1,n<<1) A[i]=A[i-1]+(p[i]? 1:-1);
    	For(i,1,n) if(p[i]) ++cnt[A[i]],now+=A[i]>A[0];
    	For(i,1,n) {
    		if(p[i]) {
    			now-=cnt[A[i]];//from A[i]-1 to A[i]
    			--cnt[A[i]];
    			++cnt[A[i+n]];
    			now+=A[i+n]>A[i];
    		}
    		else {
    			now+=cnt[A[i]+1];//from A[i]+1 to A[i]
    			if(now==0) ans1=i;
    			if(now==S) ans2=i;
    			if(now==k-S) ans3=i;
    		}
    	}
    	printf("%d
    %d
    %d
    ",ans1,ans2,ans3);
    	return 0;
    }
    

    吉夫特

    想了很久,还是只会n^2暴力,翻开洛谷题解:

    what? 众所周知,只有我不知道?

    知道这玩意就可以直接每次枚举子集啦,洛谷上是可以过的,但是bz上就要T掉啦。

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=3e5+7;
    ll mod=1e9+7;
    ll n,a[maxn],p[maxn],dp[maxn];
    
    char cc; ll ff;
    template<typename T>void read(T& aa) {
    	aa=0;cc=getchar();ff=1;
    	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    int main() {
    	read(n);
    	For(i,1,n) read(a[i]),p[a[i]]=i;
    	For(i,1,n) {
    		dp[0]+=dp[i]%=mod;
    		for(int j=a[i];j;j=(j-1)&a[i]) 
    			if(p[j]>i) dp[p[j]]+=dp[i]+1;
    	}
    	printf("%lld
    ",dp[0]%mod);
    	return 0;
    }
    

    所以应该怎么办呢

    我们如果枚举子集复杂度大约是3^18的,但是如果我们把18位分成两半,各9位,然后一个是求答案时枚举补集,一个是更新dp数组,枚举子集,就优秀多了

    具体意思见代码

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=3e5+7,maxt=(1<<9)+7,U=(1<<9)-1;
    ll mod=1e9+7;
    ll n,o,x,y,dp[maxt][maxt],ans,now;
    
    char cc; ll ff;
    template<typename T>void read(T& aa) {
        aa=0;cc=getchar();ff=1;
        while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
    
    void mo(ll& x) {if(x>=mod) x-=mod;}
    
    int main() {
        read(n);
        For(i,1,n) {
        	read(o); x=o>>9; y=o&U; now=0;
        	for(int j=x;j<=U;j=(j+1)|x) now+=dp[j][y];
        	now%=mod; ans+=now; now++; mo(now);
        	for(int j=y;;j=(j-1)&y) {
        		dp[x][j]+=now,mo(dp[x][j]);
        		if(j==0) break;
        	}
        }
        printf("%lld
    ",ans%mod);
        return 0;
    }
    
  • 相关阅读:
    C++中的string和stringstream用法1
    回调函数简析
    Qt界面设计更新
    C/C++中的类型转换
    桥接模式 bridge pattern
    装饰者模式
    适配器模式
    代理模型
    工厂类---抽象工厂(3)
    [效率神技]Intellij 的快捷键和效率技巧|系列一|常用快捷键
  • 原文地址:https://www.cnblogs.com/Serene-shixinyi/p/8977653.html
Copyright © 2011-2022 走看看