四川大学2021SCUACM新生赛决赛大部分题解(差分dp线段树……)

Posted hans774882968

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四川大学2021SCUACM新生赛决赛大部分题解(差分dp线段树……)相关的知识,希望对你有一定的参考价值。

传送门

这套题至少要过8题才能算是有脑这种器官,过9题才是正常人。很可惜我没有脑~

B

这题我比赛后学会的,还没补代码。

首先考虑对查询按x升序排序。我们的主要思想是填平之前求出的结果与当前问题的差距。这里”差距“指的是一个数据结构,它就是一个数组b[],处理到当前查询的时候,b[i]表示max(a[i~前一个询问的x])

如何填平差距?设第一个a值大于i的点为L[i],设当前查询的参数为curL,curR,curX。则[L[i]+1~i]max(a[i~curX])都是a[i],而[1~L[i]]max(a[i~curX])都不是a[i]。因此[1~L[i]]b数组信息可复用,对b[L[i]+1~i]进行区间覆盖即可。当前查询的答案就是sum(b[curL,curR])

L数组就是个简单的单调栈模板。而b数组的维护,使用线段树即可。

D

拆位+差分。首先各个位互不影响,拆位是显然的。拆位后发现区间or值为1的那些信息没有用。所以差分求出必选为0的所有点即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 1e6 + 5;
const int mod = 1e9 + 7;
int dx[4] = -1,1,0,0,dy[4] = 0,0,-1,1;

int n,m,L[N],R[N],x[N],d[N];

void dbg()puts("");
template<typename T, typename... R>void dbg(const T &f, const R &... r) 
    cout << f << " ";
    dbg(r...);

template<typename Type>inline void read(Type &xx)
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;


int main(int argc, char** argv) 
    while(~scanf("%d%d",&n,&m))
        rep(i,1,m)
            read(L[i]);read(R[i]);read(x[i]);
        
        LL ans = 0;
        re_(i,0,30)
            rep(j,1,n+1) d[j] = 0;
            rep(j,1,m)
                if(x[j]>>i&1) continue;
                d[L[j]]++;d[R[j]+1]--;
            
            rep(j,1,n) d[j] += d[j-1];
            int u = 0;
            rep(j,1,n) u += (d[j] == 0);
            ans += u*(1LL<<i);
        
        printf("%lld\\n",ans);
    
    return 0;

E

题目名叫”羊工八刀“似乎是提醒我们用差分。但我是dp做的。设当前点为x,则当前点的贡献:

sum((x-x[j])^2) = t*x*x + sum(x[j]^2) - 2*x*sum(x[j])。因此维护3个dp数组即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 1e6 + 5;
const int mod = 1e9 + 7;
int dx[4] = -1,1,0,0,dy[4] = 0,0,-1,1;

int n;LL c[N],s1[N],s2[N],dp[N];char s[N];

void dbg()puts("");
template<typename T, typename... R>void dbg(const T &f, const R &... r) 
    cout << f << " ";
    dbg(r...);

template<typename Type>inline void read(Type &xx)
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;


int main(int argc, char** argv) 
    int T;read(T);
    while(T--)
        read(n);
        scanf("%s",s+1);
        rep(i,1,n) c[i] = s1[i] = s2[i] = dp[i] = 0;
        rep(i,1,n)
            c[i] = c[i-1] + (s[i] == '1');
            s1[i] = s1[i-1] + (s[i] == '1' ? i : 0);
            s2[i] = s2[i-1] + (s[i] == '1' ? 1LL*i*i%mod : 0);
            if(s[i] != '1') continue;
            dp[i] = ((c[i]*i%mod*i%mod+s2[i]-2*i*s1[i]%mod)%mod+mod)%mod;
        
        printf("%d\\n",accumulate(dp+1,dp+n+1,0,[](int tot,int v)return (tot+v)%mod;));
    
    return 0;

F

小模拟。题干容易引起歧义,尤其是对于真的玩过植物大战僵尸的。比赛时猜出的题目要求是:不能有植物可被偷走。所以做法就是看每一个植物是否有可能被偷走,很水……

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 8,M = 11;
const int INF = 1e9;
int dx[9] = -1,-1,0,1,1,1,0,-1,0,dy[9] = 0,1,1,1,0,-1,-1,-1,0;

int n;
char a[N][M];bool prot[N][M];int val[N][M];

void dbg()puts("");
template<typename T, typename... R>void dbg(const T &f, const R &... r) 
    cout << f << " ";
    dbg(r...);

template<typename Type>inline void read(Type &xx)
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;


bool out(int x,int y)
    return x < 0 || x >= 6 || y < 0 || y >= 9;


bool solve()
    memset(prot,0,sizeof prot);
    memset(val,0,sizeof val);
    re_(i,0,6) re_(j,0,9) if(a[i][j] == '3')
        re_(t,0,9)
            int x = i+dx[t],y = j+dy[t];
            if(out(x,y)) continue;
            prot[x][y] = true;
        
    
    re_(i,0,6) re_(j,0,9) if(a[i][j] == '4' || a[i][j] == '0') prot[i][j] = true;
    re_(i,0,6) re_(j,0,9)
        if(a[i][j] == '1')
            rep(k,j+1,min(8,j+4)) val[i][k]++;
        
        if(a[i][j] == '2')
            re_(t,0,9)
                int x = i+dx[t],y = j+dy[t];
                if(out(x,y)) continue;
                val[x][y]++;
            
        
    
    // re_(i,0,6) re_(j,0,9) cout<<(prot[i][j]?'x':a[i][j])<< " \\n"[j==8];//
    // re_(i,0,6) re_(j,0,9) cout<<(prot[i][j]?-1:val[i][j])<< " \\n"[j==8];//
    re_(i,0,6) re_(j,0,9)
        if(prot[i][j]) continue;
        if(val[i][j] < 2) return false;
    
    return true;


int main(int argc, char** argv) 
    int T;read(T);
    while(T--)
        re_(i,0,6) scanf("%s",a[i]);
        puts(solve() ? "Yes" : "No");
    
    return 0;

G

这题和校内题不同。校内题是问int范围的最大质数,签到。这题是问[2,x]范围的最大质数。int范围内素数间距应该不太大,大概直接暴力是没问题的。我用的素性测试。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_2016广东工业大学新生杯决赛网络同步赛暨全国新生邀请赛 题解&源码

2021年第十二届蓝桥杯大赛软件赛决赛C/C++大学A组 个人部分题解

SCUACM22暑假集训前劝退赛部分题解

2016广东工业大学新生杯决赛网络同步赛暨全国新生邀请赛

2021-2022 北京化工大学程序设计新生赛 - 问题 N: 聪明的蚂蚁 - 题解

华南师大 2017 年 ACM 程序设计竞赛新生初赛题解