无题十三

题解:

第一题:异或的老套路,按位算贡献,统计某一位有多少个数为1,就用数位DP好了;

#include<bits/stdc++.h>
using namespace std;
const int M = 1e6;
#define ll long long 
const ll mod = 1e9 + 7;
int L, R, dp[35][2];
int bin[33], cnt[33], digit[33];
inline ll moc(ll a){return a >= mod ? a - mod : a;}
void solve1(){
    ll ans = 0;
    for(int i = L; i <= R; i++)
        for(int j = L; j <= R; j++)
            ans = moc(ans + (i ^ j) % mod);
    printf("%lld\n", ans);
}

int dfs(int dep, int s, bool f, int sum){
    if(!dep) return sum;
    if(!f && dp[dep][sum] != -1) return dp[dep][sum];
    int i = f ? digit[dep] : 1;
    int tmp = 0;
    for(; i >= 0; i--){
        if(dep == s && i) tmp += dfs(dep - 1, s, f & (i == digit[dep]), sum + 1);
        else tmp += dfs(dep - 1, s, f & (i == digit[dep]), sum);
    }
    if(f) return tmp;
    return dp[dep][sum] = tmp;
}

void get(int x, ll f){
    int d = 0;
    if(x < 0) return;
    while(x){
        digit[++d] = x & 1;
        x >>= 1;
    }
    for(int i = 1; i <= 31; i++){
        memset(dp, -1, sizeof(dp));
        cnt[i] += f * dfs(d, i, 1, 0); 
    }
}

void solve3(){
    memset(cnt, 0, sizeof(cnt));
    get(L-1, -1);
    get(R, 1);
    ll ans = 0;
    ll cc = R - L + 1;
    for(int j = 1; j <= 31; j++) ans = moc(ans + 1LL*bin[j] * cnt[j] % mod * (cc - cnt[j]) * 2 % mod);
    printf("%lld\n", ans);
    
}

int main(){
    freopen("xor.in","r",stdin);
    freopen("xor.out","w",stdout);
    int T;
    scanf("%d", &T);
    bin[1] = 1;
    for(int i = 2; i <= 31; i++) bin[i] = bin[i-1] << 1;
    while(T--){
        scanf("%d%d", &L, &R);
        solve3();
    }
}
View Code

第二题:博弈论;

两堆石子:

 

上面的表总结一下就是:我们知道(x, y)是必输状态,那么(x, z) z != y一定是必赢状态;

分类讨论: z>y,我一定可以一步转移到(x,y) ; z<y, 如果(x,z)必输,那么(x,y)就可以转移到他,成为必赢状态,与已知条件矛盾;

所以一个x,对应一个y, y‘'-x''的值相同时时,都是必赢状态;

我们推广到三个状态:

我们枚举每次选几个;

(x, y, z)是已知必输状态;

一次选两堆,如果x和原来相同,那么y''-z'' = y-z,一定是必赢状态,所以一堆确定时,令两堆差值和原来一样,我们就可以把他筛掉;

一次选一堆,这是确定x,y,我们就可以把z''转化为z,我们就只需要看f(x,y)这个状态是否为z;

一次选三堆,同时前去一个数达到(x,y,z),他就必赢,要求就是三个数之间的相差值同;

我们从小往大枚举,不断筛数,没有被筛掉的他就必败,这样复杂度N^3

#include<bits/stdc++.h>
using namespace std;
bool dp[305][305][305], mp[305][305][3];
int a[4];
void init(){
    for(int i = 0; i <= 300; i++)
        for(int j = 0; j <= i; j++)
            for(int k = 0; k <= j; k++){
                bool ok = 1;
                if(mp[i][j-k][0])ok=0;
                if(mp[j][i-k][0])ok=0;
                if(mp[k][i-j][0])ok=0;
                if(mp[i][j][1])ok=0;
                if(mp[i][k][1])ok=0;
                if(mp[j][k][1])ok=0;
                if(mp[i-j][j-k][2])ok=0;
                if(ok){
                    dp[i][j][k]=1;
                    mp[i][j-k][0] = mp[j][i-k][0] = mp[k][i-j][0] = mp[i][j][1] = mp[i][k][1] = mp[j][k][1] = mp[i-j][j-k][2] = 1;
                }
            }
}

int main(){
    freopen("stone.in","r",stdin);
    freopen("stone.out","w",stdout);
    int T;
    scanf("%d", &T);
    init();
    while(T--){
        int x, y, z;
        scanf("%d%d%d", &a[0], &a[1], &a[2]);
        sort(a, a+3);
        if(dp[a[2]][a[1]][a[0]])puts("No");
        else puts("Yes");
    }
}
View Code

第三题:神奇的DP定义;

 以上可以2,0,-2序列可以自己手动画画证明;

第一个优化:除了两端中间都可以随便取;

 我们已经证明了中间的系数是+2,-2,0,且两个2之间有一个-2,我们如果加上一段数,他大于0就往2中何必,小于0就往-2中合并,或者合并到0中,对答案只增不减;

dp[i][j][k]表示处理到第i个数,分成了j段,处于k阶段

阶段定义:0:处于系数为2

     1:处于2后面的0

     2:处于系数为-2

     3:处于-2后的0

对于,我们可以和前面一起dp[i-1][j][0],可以自成一段,dp[i-1][j-1][3/2],就这样转移好了;

对于两端的系数特殊变成1、-1,中间枚举转移;

初值dp[i][0][1/3] = 0, 其余为-inf,表示开头可以随便从一个地方开始;

答案在dp[i ( i > K) ][K] [0 / 2] 中取max,表示我可以在任意地方结束;

#include<bits/stdc++.h>
using namespace std;
const int M = 4*1e4 + 5;
int read(){
    int 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*10+c-'0';c=getchar();}
    return x*=f;    
}
int dp[M][205][4], a[M];

int main(){
    freopen("optimization.in","r",stdin);
    freopen("optimization.out","w",stdout);
    int n, k;
    scanf("%d%d", &n, &k);
    for(int i = 0; i <= n; i++)
        for(int j = 0; j <= k; j++)
            for(int z = 0; z < 4; z++) dp[i][j][z] = -1e9;
    for(int i = 0; i <= n; i++) dp[i][0][1] = dp[i][0][3] = 0;
    for(int i = 1; i <= n; i++){
        a[i] = read();
        for(int j = 1; j <= k; j++){
            int xs = 2 - (j == 1 || j == k);
            dp[i][j][0] = max(dp[i-1][j][0], max(dp[i-1][j-1][3], dp[i-1][j-1][2])) + xs * a[i];
            dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0]);
            dp[i][j][2] = max(dp[i-1][j][2], max(dp[i-1][j-1][0], dp[i-1][j-1][1])) - xs * a[i];
            dp[i][j][3] = max(dp[i-1][j][3], dp[i-1][j-1][2]);
            if(j != 1){
                dp[i][j][1] = max(dp[i-1][j-1][1], dp[i][j][1]);
                dp[i][j][3] = max(dp[i-1][j-1][3], dp[i][j][3]);
            }
        }
    } 
    int ans = -1e9;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= k; j++) ans = max(dp[i][k][0], max(dp[i][k][2], ans));
    printf("%d\n", ans);
}
View Code

猜你喜欢

转载自www.cnblogs.com/EdSheeran/p/9805277.html
今日推荐