csps模擬86異或,取石子,優化題解

題面:https://www.cnblogs.com/Juve/articles/11736440.htmlhtml

異或:ios

考試時只想出了暴力ide

咱們能夠對於二進制下每一位w,求出[l,r]中有幾個數在這一位是1,記爲x,設y表示[l,r]中有幾個數在w位不是一函數

這樣就會有x×y對數在w位上產生貢獻,每一對數會有2w的貢獻,優化

主要就是實現一個calc函數,calc(x,i)表示從0到x有多少的數二進制下第i位是1,而後咱們發現一個規律:spa

若是把0~9二進制打印出來:code

9001001
8001000
7000111
6000110
5000101
4000100
3000011
2000010
1000001
0000000

發現每一位是循環的,第0位循環節是2。第1位是4,第2位是8,並且只有沒一個循環節的後一半是1,因此根據這個咱們實現了calc函數htm

 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\n",2*ans%mod);
47     }
48     return 0;
49 }
View Code

取石子:blog

第一次作博弈論,而後我考試AC了。。。get

其實不能算是裸的博弈,畢竟我認爲是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\n",max(f[n][k][2],f[n][k][3]));
	return 0;
}
相關文章
相關標籤/搜索