题面:https://www.cnblogs.com/Juve/articles/11736440.html
异或:
考试时只想出了暴力
我们可以对于二进制下每一位w,求出[l,r]中有几个数在这一位是1,记为x,设y表示[l,r]中有几个数在w位不是一
这样就会有x×y对数在w位上产生贡献,每一对数会有2w的贡献,
主要就是实现一个calc函数,calc(x,i)表示从0到x有多少的数二进制下第i位是1,然后我们发现一个规律:
如果把0~9二进制打印出来:
9:001001 8:001000 7:000111 6:000110 5:000101 4:000100 3:000011 2:000010 1:000001 0:000000
发现每一位是循环的,第0位循环节是2。第1位是4,第2位是8,而且只有没一个循环节的后一半是1,所以根据这个我们实现了calc函数

1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define int long long 6 #define re register 7 using namespace std; 8 const int mod=1e9+7; 9 int t,l,r,ans; 10 inline int q_pow(re int a,re int b,re int p){ 11 re int res=1; 12 while(b){ 13 if(b&1) res=res*a%p; 14 a=a*a%p; 15 b>>=1; 16 } 17 return res; 18 } 19 int qpow(int a,int b){ 20 int res=1; 21 while(b){ 22 if(b&1) res=res*a; 23 a=a*a; 24 b>>=1; 25 } 26 return res; 27 } 28 int calc(int x,int pos){ 29 ++x; 30 int res=0; 31 int tmp=qpow(2,pos+1); 32 int q=x/tmp; 33 res+=q*tmp/2; 34 int p=x%tmp; 35 if(p>tmp/2) res+=p-tmp/2; 36 return res; 37 } 38 signed main(){ 39 scanf("%lld",&t); 40 while(t--){ 41 scanf("%lld%lld",&l,&r); 42 ans=0; 43 for(int i=0;i<=31;++i){ 44 ans=(ans+(r-l+1-(calc(r,i)-calc(l-1,i)))*(calc(r,i)-calc(l-1,i))%mod*q_pow(2,i,mod)%mod)%mod; 45 } 46 printf("%lld ",2*ans%mod); 47 } 48 return 0; 49 }
取石子:
第一次做博弈论,然后我考试AC了。。。
其实不能算是裸的博弈,毕竟我认为是dp
一开始打搜索,发现不会打,打了2个多小时,然后突然发现可以dp筛出状态,然后打了正解,最后30分钟交上去AC了
设g[i][j][k]表示三堆石子数量为i,j,k时能否先手必胜
我们发现如果有x,y,z必输,那么x,y,z+k;
x+k,y,z;
x,y+k,z;
x+k,y+k,z;
x+k,y,z+k;
x,y+k,z+k;
x+k,y+k,z+k一定必胜
因为先手可以通过x+k,y+k,z+k都拿走k,变成x,y,z从而让对手必输
然后我们就可以转移了,从0,0,0开始,如果找到了一个必输的,那么用它更新后面必胜的,有些类似线性筛
虽然有4层循环,但是一般不会进第4个循环,就像线性筛一样,总的复杂度还是O(n3)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define re register using namespace std; int t,x,y,z; bool g[305][305][305]; inline void pre(){ for(re int i=0;i<=300;++i){ for(re int j=0;j<=300;++j){ for(re int k=0;k<=300;++k){ if(g[i][j][k]) continue; for(re int p=1;p<=300;++p){ re int a=min(301,i+p),b=min(301,j+p),c=min(301,k+p); if(a+b+c>=903) break; g[a][j][k]=g[i][b][k]=g[i][j][c]=1; g[a][b][k]=g[a][j][c]=g[i][b][c]=1; g[a][b][c]=1; } break; } } } } signed main(){ pre(); scanf("%d",&t); while(t--){ scanf("%d%d%d",&x,&y,&z); if(g[x][y][z]) puts("Yes"); else puts("No"); } return 0; }
优化:
看到绝对值要想着去绝对值
我们让每一个数都必选,那么每一个数a对整个答案的贡献可能是2a,-2a,a,-a,0
a和-a之存在与第一段和最后一段,系数是2就是a所在的区间比它左右区间的元素的和都大,具体来说就是:
2的情况:
$|s_{i-1}-s_i|+|s_i-s_{i+1}|=2*s_i-s_{i-1}-s_{i+1}$,
-2的情况和2的相反
0的情况:
$|s_{i-1}-s_i|+|s_i-s_{i+1}|=s_{i-1}-s_i+s_i-s_{i+1}$或$|s_{i-1}-s_i|+|s_i-s_{i+1}|=s_i-s_{i-1}-s_i+s_{i+1}$
然后就可以dp转移了,设f[i][j][4]表示前i个,划分了j个区间的最大值,
我们定义正为上升,负为下降,那么0表示上升,1表示下降,2表示从上升到下降,3表示从下降到上升
然后狗shi的转移:
1 if(j==1||j==k){ 2 f[i][j][0]=max(f[i-1][j][0],f[i-1][j-1][2])+a[i]; 3 f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][3])-a[i]; 4 f[i][j][2]=max(f[i-1][j][2],f[i][j][1]); 5 f[i][j][3]=max(f[i-1][j][3],f[i][j][0]); 6 }else{ 7 f[i][j][0]=max(f[i-1][j][0],f[i-1][j-1][2])+2*a[i]; 8 f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][3])-2*a[i]; 9 f[i][j][2]=max(f[i-1][j-1][2],max(f[i-1][j][2],f[i][j][1])); 10 f[i][j][3]=max(f[i-1][j-1][3],max(f[i-1][j][3],f[i][j][0])); 11 }
其实挺好想的,考虑一下实际情况就好理解了,1和k的情况单独拿出来转移,因为他们的系数为1和-1
最终答案就是max(f[n][k][2],f[n][k][3])
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define re register #define int long long using namespace std; const int MAXN=3e4+5; int n,k,a[MAXN],f[MAXN][205][4]; signed main(){ scanf("%lld%lld",&n,&k); for(re int i=1;i<=n;++i) scanf("%lld",&a[i]); memset(f,-0x3f,sizeof(f)); for(int i=0;i<=n;++i) for(int j=0;j<4;++j) f[i][0][j]=0; for(int i=1;i<=n;++i){ int N=min(i,k); for(int j=1;j<=N;++j){ if(j==1||j==k){ f[i][j][0]=max(f[i-1][j][0],f[i-1][j-1][2])+a[i]; f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][3])-a[i]; f[i][j][2]=max(f[i-1][j][2],f[i][j][1]); f[i][j][3]=max(f[i-1][j][3],f[i][j][0]); }else{ f[i][j][0]=max(f[i-1][j][0],f[i-1][j-1][2])+2*a[i]; f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][3])-2*a[i]; f[i][j][2]=max(f[i-1][j-1][2],max(f[i-1][j][2],f[i][j][1])); f[i][j][3]=max(f[i-1][j-1][3],max(f[i-1][j][3],f[i][j][0])); } } } printf("%lld ",max(f[n][k][2],f[n][k][3])); return 0; }