[CSP-S模擬測試86]題解

很久沒有寫整套題的題解了呢……主要是這兩天考試題愈發神仙 實在是超出了垃圾博主的能力範圍啊QAQios

A.異或

不難想到,若是咱們獲得了$[L,R]$中每一位上0和1的個數,那麼答案即爲$2 \times \sum \limits _{i=0} ^{\log R} num0[i]\times num1[i] \times 2^i$git

因此能夠獲得一個暴力的思路,枚舉$[L,R]$中的每一個數按位統計。優化

如今瓶頸在於區間每一位的0/1個數的統計。若是咱們把連續的數寫成二進制表示,能夠發現一些規律:spa

000000000000 //0
000000000001 //1
000000000010 //2
000000000011 //3
000000000100 //4
000000000101 //5
000000000110 //6
000000000111 //7
000000001000 //8
000000001001 //...
000000001010
000000001011
000000001100
000000001101
000000001110
000000001111

 

縱向對比。顯然,對於一段連續的整數,它們每一位上的0/1分佈是有循環節的,而且循環節內部前一半是0,後一半是1,且循環節長度爲$2^{i+1}$。rest

因此咱們能夠輕鬆地求出從0到某個數這段區間裏每一位的0/1 個數,至關與一個前綴和,$sum[R]-sum[L-1]$一下就行了。blog

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int T,L,R;
int bit[35][3];
void work()
{
    int L=read(),R=read();
    for(int i=0;i<31;i++)
    {
        bit[i][0]=bit[i][1]=0;
        int cir=(R+1)/(1<<i+1);
        bit[i][0]+=cir*(1<<i);
        bit[i][1]+=cir*(1<<i);
        int rest=(R+1)%(1<<i+1);
        bit[i][0]+=min(rest,1<<i);
        bit[i][1]+=max(0,rest-(1<<i));
        cir=(L)/(1<<i+1);
        bit[i][0]-=cir*(1<<i);
        bit[i][1]-=cir*(1<<i);
        rest=(L)%(1<<i+1);
        bit[i][0]-=min(rest,1<<i);
        bit[i][1]-=max(0,rest-(1<<i));
    }
    ll ans=0;
    for(int i=0;i<31;i++)
        (ans+=1LL*bit[i][0]*bit[i][1]%mod*(1LL<<i)%mod)%=mod;
    printf("%lld\n",ans*2%mod);
}

int main()
{
    T=read();
    while(T--)work();
    return 0;
}

 

B.取石子

首先,最優策略下,先手必敗的下一步必定是先手必勝。ci

能夠用相似於篩法(或者說dp也同樣)獲得全部先手必敗的局面,從而獲得答案。直接轉移是$O(n^4)$的。get

不難想到,給定$x,y$以後,使得$(x,y,z)$爲先手必敗態的$z$只有一個。string

 

 

 而後就能夠$O(n^3)$了。it

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=605,base=300;
bool win[base+5][base+5][base+5];
bool f[4][N][N],g[4][N][N],a[N][N];
int read()
{
	int x=0,f=1;char ch=getchar();


	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int T,x,y,z;
bool judge(int i,int j,int k)
{
	if(a[j-i+base][k-j+base])return 1;
	if(f[0][j][k])return 1;
	if(f[1][i][k])return 1;
	if(f[2][i][j])return 1;
	if(g[0][i][k-j+base])return 1;
	if(g[1][j][k-i+base])return 1;
	if(g[2][k][j-i+base])return 1;
	return 0;
}
void ini()
{
	for(int i=0;i<=base;i++)
		for(int j=0;j<=base;j++)
			for(int k=0;k<=base;k++)
			{
				if(judge(i,j,k))win[i][j][k]=1;
				else
				{
					a[j-i+base][k-j+base]=1;
					f[0][j][k]=f[1][i][k]=f[2][i][j]=1;
					g[0][i][k-j+base]=g[1][j][k-i+base]=g[2][k][j-i+base]=1;
				}
				//cout<<i<<' '<<j<<' '<<k<<' '<<win[i][j][k]<<endl;
			}
}
void work()
{
	x=read();y=read();z=read();
	if(win[x][y][z])puts("Yes");
	else puts("No");
}
int main()
{
	//freopen("my.out","w",stdout);
	ini();
	T=read();
	while(T--)work();
	return 0;
}

 

C.優化

性質1:絕對值能夠直接拆,不會使答案變差。

性質2:除了開頭和結尾,中間幾段都緊密相連顯然最優。

先把絕對值扔了,而後把每一個$s_i$單獨拿出來,考慮它的貢獻。

除了$s_1,s_k$以外,每一個$s$產生的係數只能爲0或2或-2。

進一步觀察還能夠發現,兩個係數爲2的$s$之間,必然存在一段係數爲-2的$s$。

係數爲0的能夠自由穿插在2和-2之間,因此咱們能夠把階段劃分紅4種。最高點(2),最低點(-2),上升段(-2~2),降低段(2~-2)。

那麼就能夠轉移了,看似要$O(n^2 k)$或者$O(n^3 k)$,但考慮到每一段內部每一個數的係數都是同樣的,因此直接對於每一個點判斷它在不在這裏斷段就行了。

$a[]$中可能有負數,因此初始化須要$-inf$。

$O(nk)$。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=3e4+5;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,a[N],K;
int dp[N][202][5];
int main()
{
    n=read();K=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    memset(dp,-0x3f,sizeof(dp));
    for(int i=0;i<=n;i++)
        for(int j=0;j<4;j++)
            dp[i][0][j]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=min(K,i);j++)
        {
            int p=(j==1||j==K)?1:2;
            dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j-1][2])+p*a[i];
            dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][3])-p*a[i];
            dp[i][j][2]=max(dp[i-1][j][2],dp[i][j][1]);
            if(j!=1&&j!=K)dp[i][j][2]=max(dp[i][j][2],dp[i-1][j-1][2]);
            dp[i][j][3]=max(dp[i-1][j][3],dp[i][j][0]);
            if(j!=1&&j!=K)dp[i][j][3]=max(dp[i][j][3],dp[i-1][j-1][3]);
            /*cout<<i<<' '<<j<<endl;
            for(int k=0;k<4;k++)cout<<dp[i][j][k]<<' ';
            puts(" ");*/
        }
    }
    int ans=max(dp[n][K][2],dp[n][K][3]);
    printf("%d\n",ans);
    return 0;
}
相關文章
相關標籤/搜索