四川大学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_(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 = 1e3 + 5;
const int INF = 1e9;
int dx[4] = {-1,1,0,0},dy[4] = {0,0,-1,1};

int n,a[N],b[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;
}


LL Mult_Mod(LL a,LL b,LL m)//res=(a*b)%m
{
    a%=m;
    b%=m;
    LL res=0;
    while(b)
    {
        if(b&1)
            res=(res+a)%m;
        a=(a<<=1)%m;
        b>>=1;
    }
    return res%m;
}
LL Pow_Mod(LL a, LL b, LL m)//res=(a^b)%m
{
    LL res=1;
    LL k=a;
    while(b)
    {
        if((b&1))
            res=Mult_Mod(res,k,m)%m;
 
        k=Mult_Mod(k,k,m)%m;
        b>>=1;
    }
    return res%m;
}
bool Witness(LL a,LL n,LL x,LL sum)
{
    LL judge=Pow_Mod(a,x,n);
    if(judge==n-1||judge==1)
        return 1;
    while(sum--)
    {
        judge=Mult_Mod(judge,judge,n);
        if(judge==n-1)
            return 1;
    }
    return 0;
}
bool Miller_Rabin(LL n)
{
    if(n<2)
        return 0;
    if(n==2)
        return 1;
    if((n&1)==0)
        return 0;
    LL x=n-1;
    LL sum=0;
    while(x%2==0)
    {
        x>>=1;
        sum++;
    }
    int times=20;
    for(LL i=1;i<=times;i++)
    {
        LL a=rand()%(n-1)+1;//取与p互质的整数a
        if(!Witness(a,n,x,sum))//费马小定理的随机数检验
            return 0;
    }
    return 1;
}

int main(int argc, char** argv) {
    read(n);
    int ans = n;
    for(;;--ans){
        if(Miller_Rabin(ans)) break;
    }
    printf("%d\n",ans);
    return 0;
}
H

签到题。

#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 = 1e3 + 5;
const int INF = 1e9;
int dx[4] = {-1,1,0,0},dy[4] = {0,0,-1,1};

int n,a[N],b[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) {
    read(n);
    int ans = 1;
    re_(i,0,n) read(a[i]);
    re_(i,0,n) read(b[i]);
    re_(i,0,n){
        ans *= max(0,b[i]-a[i]+1);
    }
    printf("%d\n",ans);
    return 0;
}
I

大模拟。注意坑:

  • 跳转类型的指令,jz、jnz、jg,不管是否真的执行,都需要判定是不是越界(正确率90%就是错这里)。
  • 越界的标准是大于n(停机指令是n号)(这个是最坑的……我感觉比赛中可能要用assert触发RE这种手段来试出出题人的真实意图)。

我构思的时候把reg和mem分开了,写代码的时候有亿点后悔……

n,m = list(map(int,input().split(" ")))
inst = []
for _ in range(n):
    tmp = input().split(" ")
    for i in range(1,len(tmp)): tmp[i] = int(tmp[i])
    inst.append(tmp)
inp = list(map(int,input().split(" ")))
inp.reverse()
reg = [0] * 16
mem = [0] * m
outs = []

def solve():
    global inp,reg,mem,outs
    idx = 0
    getOp = lambda num: inst[idx][1:1+num] if num > 1 else inst[idx][1]
    ob0 = lambda v: not (0 <= v and v <= 15)
    ob1 = lambda v: not (1 <= v and v <= 15)
    while idx < n:
        if inst[idx][0] == "in":
            reg[15] = inp.pop()
        elif inst[idx][0] == "out":
            outs.append(reg[15])
        elif inst[idx][0] == "mov":
            a,b = getOp(2)
            if a < 0 or b <= 0: return False
            try:
                val = reg[a] if a < 16 else mem[a-16]
                if b >= 16: mem[b-16] = val
                else: reg[b] = val
            except IndexError:
                return False
        elif inst[idx][0] == "add":
            a,b,c = getOp(3)
            if ob1(a) or ob0(b) or ob0(c): return False
            reg[a] = reg[b] + reg[c]
        elif inst[idx][0] == "cmp":
            a,b = getOp(2)
            if ob0(a) or ob0(b): return False
            reg[14] = 1 if reg[a] == reg[b] else (2 if reg[a] > reg[b] else 0)
        elif inst[idx][0] == "jz":
            a = getOp(1)
            if a < 0 or a > n: return False
            if reg[14] == 1:
                idx = a-1
        elif inst[idx][0] == "jnz":
            a = getOp(1)
            if a < 0 or a > n: return False
            if reg[14] != 1:
                idx = a-1
        elif inst[idx][0] == "jg":
            a = getOp(1)
            if a < 0 or a > n: return False
            if reg[14] == 2:
                idx = a-1
        else: return False
        idx += 1
    return True

fl = solve()
print(*outs)
if not fl: print("error")
J

简易计算器,不会出现-1+2这种取反的情况。我用递归下降做的。关键是中间结果可以到ULL范围,不知道他们怎么解决的。

idx = 0
s = ""

def cur():
    global idx
    while idx < len(s) and s[idx] == " ": idx += 1
    return "#" if idx >= len(s) else s[idx]

def advance():
    global idx
    while idx < len(s) and s[idx] == " ": idx += 1
    if not s[idx].isdigit():
        idx += 1
        return s[idx-1]
    v = 0
    while idx < len(s) and s[idx].isdigit():
        v = 10*v+ord(s[idx])-ord('0')
        idx += 1
    return v

def mul():
    ret = advance()
    while cur() == 'x' or cur() == '/':
        op = advance()
        if op == 'x': ret *= advance()
        else: ret //= advance()
    return ret

def add():
    ret = mul()
    while cur() == '+' or cur() == '-':
        op = advance()
        if op == '+': ret += mul()
        else: ret -= mul()
    return ret

T = int(input())
for cas in range(T):
    s = input()
    idx = 0
    print(add())
K

钓鱼题……发现直接原路返回是不违反题意的,所以就是维护连通块而已……这题极其水,可能大多数人(包括我QAQ)都被骗了~

#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;

int n,m,k,fa[N];bool isP[N],val[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;
}
void read(){}
template<typename T,typename ...R>void read(T &x,R &...r){
    read(x);
    read(r...);
}

int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}

int main(int argc, char** argv) {
    read(n,m,k);
    rep(i,1,n) fa[i] = i;
    rep(i,1,k){
        int x;read(x);
        isP[x] = true;
    }
    rep(_,1,m){
        int x,y,w;read(x,y,w);
        fa[find(x)] = find(y);
    }
    rep(i,1,n) if(isP[i]) val[find(i)] = true;
    printf("%d\n",accumulate(val+1,val+n+1,0));
    return 0;
}
L

最讨厌几何和分类讨论题,这题偏偏是交集……幸好比赛时过了……

首先直接转到第一象限是没问题的。

分类讨论:

  • 首先特判已经在圆内的情况。答案为0。
  • 然后走若干步,到不足一步就可以进圆的情况,记为ans。
  • 如果圆半径太小,导致再走一步总会出界,就应该回退一步再走两步,答案为ans+1(如果ans==0,即无法回退,答案为2)。
  • 否则再走一步进圆即可,答案为ans+1。
#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 double EP = 1e-11;
int dx[4] = {-1,1,0,0},dy[4] = {0,0,-1,1};

LL x,y,r,k;

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 eq(double v){return fabs(v) < EP;}

int solve(){
    if(x*x+y*y <= r*r) return 0;
    double d = sqrt(x*x+y*y)-r;
    int ans = int(floor(d/k));
    d -= ans*k;
    if(eq(d)) return ans;
    if(d+2*r >= k) return ans+1;
    if(!ans) return 2;
    return ans+1;
}

int main(int argc, char** argv) {
    int T;read(T);
    while(T--){
        read(x);read(y);read(r);read(k);
        printf("%d\n",solve());
    }
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值