A. 玉兔祥云


$\tt{J\textcolor{red}{Yawa}}$

$\tt{J\textcolor{red}{Yawa}}$

$\tt{J\textcolor{red}{Yawa}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

Sol

对于第一问,考虑抽屉原理。

对于第二问,考虑贪心。

Code

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int x, y;
    cin >> x >> y;
    cout << max(0LL, y - 9LL * x)
         << ' ' << y / 10 << endl;
    return 0;
}

B. 视界巡航


$\tt{z\textcolor{red}{js123}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{z\textcolor{red}{js123}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{B\textcolor{red}{yGones}\ and\ \tt{c\textcolor{red}{hufuzhe}}}$

Sol

阅读题意可知,这题我们需要动态维护序列的最大值最小值。

每次操作进行修改最大最小并重排的复杂度是错误的,达到了 $\mathcal O(q\times n \log n)$ 的级别。

那么,我们不难想到 C++ 的 STL,multiset。

multiset 与 set 的区别在与,multiset 的元素是可以重复的。根据题意及样例,这里可能会有重复元素出现。

下面介绍一下 multiset:

  1. multiset 位于 set 库中。请使用 #include<set> 等或使用包含 set 库的库。
  2. multiset<type>mst 建立一个 type 类型的 multiset 容器。type 可以是 int/long long/pair<int,int>/string 等。
  3. mst.clear() 清空。
  4. mst.insert(x) 向集合插入元素 $x$。
  5. mst.empty() 查询集合是否为空。
  6. mst.begin()/mst.end() 返回集合开头/结尾迭代器。代码中所用的 *s.begin()*(--s.end()) 即为开头与结尾元素。
  7. mst.erase(x) 删除迭代器为 $x$ 的元素。

乱搞做法:平衡树。

赛时码不出来怎么办?C++ 拥有强大的 pb_ds 库,可以直接实现。但是在提交的时候请选择开启 O2 优化的语言,因为 pb_ds 在无 O2 下的表现极劣。

Code

代码

/*
Auther:  zjs123
Luogu:   754000
QQ:      755328053
*/
#include<bits/stdc++.h>
using namespace std;
int n,t;
int op;
long long x,a,b;
multiset<long long> s;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>x;
    for(int i=1;i<=n;i++){
        cin>>a;
        s.insert(a);
    }
    cin>>t;
    while(t--){
        cin>>op>>b;
        if(op==1)x=b;
        else if(op==2){
            s.erase(--s.end());
            s.insert(b);
        }
        else{
            s.erase(s.begin());
            s.insert(b);
        }
        cout<<*(--s.end())+x<<"\n";
    }
    return 0;
}

C. 恶灵群舞


$\tt{z\textcolor{red}{js123}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{z\textcolor{red}{js123}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{B\textcolor{red}{yGones}\ and\ \tt{c\textcolor{red}{hufuzhe}}}$

Sol

阅读完题意,不难联想到搜索。

因为要求出最右下角的答案,所以我们需要先找出所有答案。

考虑进行搜索,用 $z_{i,j}$ 表示走到 $(i,j)$ 的最小代价。因为每个点最多走不到很多次,穿墙的其实很少,所以时间复杂度 $\mathcal O(nm)$。

Code

代码

#include<bits/stdc++.h>
using namespace std;
struct node{
    long long x,y;
    long long va;
};
long long n,m,a,b;
long long aa[120][120];
long long h[120][120];
pair<long long,long long> vis[120][120];
long long dx[]={1,-1,0,0};
long long dy[]={0,0,1,-1};
void dfs(long long x,long long y){
    h[x][y]=1;
    for(long long i=0;i<4;i++){
        long long tx=x+dx[i];
        long long ty=y+dy[i];
        if(tx>0&&tx<=n&&ty>0&&ty<=m&&h[tx][ty]==0&&aa[tx][ty]==0)dfs(tx,ty);
    }
}
queue<node> q;
long long z[120][120];
long long ans=0x3f3f3f3f3f3f3f3f;
long long xx,yy;
main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>a>>b;
    memset(z,0x3f,sizeof z);
    for(long long i=1;i<=n;i++){
        for(long long j=1;j<=m;j++){
            cin>>aa[i][j];
        }
    }
    memset(vis,0x3f,sizeof vis);
    dfs(1,1);
    q.push(node{1,1,0});
    vis[1][1]=make_pair(1,0);
    z[1][1]=0;
    while(!q.empty()){
        long long x=q.front().x;
        long long y=q.front().y;
        long long va=q.front().va;
        q.pop();
        for(long long i=0;i<4;i++){
            long long tx=x+dx[i];
            long long ty=y+dy[i];
            if(tx>0&&tx<=n&&ty>0&&ty<=m&&aa[tx][ty]==0&&vis[tx][ty].second>va+a){
                vis[tx][ty].second=va+a;
                q.push(node{tx,ty,va+a});
                z[tx][ty]=min(z[tx][ty],va+a);
            }
            if(tx>0&&tx<=n&&ty>0&&ty<=m&&aa[tx][ty]==1&&aa[tx+dx[i]][ty+dy[i]]==0&&vis[tx+dx[i]][ty+dy[i]].second>va+b){
                tx+=dx[i];
                ty+=dy[i];
                vis[tx][ty].second=va+b;
                q.push(node{tx,ty,va+b});
                z[tx][ty]=min(z[tx][ty],va+b);
            }
        }
    }
    for(int i=n;i>=1;i--){
        for(long long j=m;j>=1;j--){
            if(ans>z[i][j]&&h[i][j]==0){
                ans=z[i][j];
                xx=i;
                yy=j;
            }
        }
    }
    if(ans==0x3f3f3f3f3f3f3f3f){
        cout<<"No";
        return 0;
    }
    cout<<"Yes\n";
    cout<<ans<<" ("<<xx<<","<<yy<<")";
    return 0; 
}

Nothing

cyx&wyr の阴谋

D. 狂热派对


$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{B\textcolor{red}{yGones}\ and\ \tt{c\textcolor{red}{hufuzhe}}}$

Sol

其实就是中等的模拟。代码只是变量比较长。

造数据才是大模拟!cyx 为了造数据更容易甚至改了题面。但尽管如此数据生成的代码与 std 的长度相差不大。

我们对于每个记录,按照题意描述检查一下,如果过了就打上标记,并计算时间差得到该题的罚分。被 hack 就取消标记。出现非 CE 或 非 WA on 1 就把这题分数按规定减去 $50$。

Code

代码

/*
Auther:  cyx2009
Luogu:   516346
QQ:      2176807108
*/
#include <bits/stdc++.h>
using namespace std;
int n, m, k;
string Problem_Name[2520];
struct Submission
{
    string Time, Verdict, Problem;
    int Minute;
} CYX_Submission[25020], ZJS_Submission[25020];
string Start_Time;
int INT_Start_Time;
long long CYX_Score, ZJS_Score;
int ZJS_WA[2520];
int CYX_WA[2520];
bool ZJS_Hacked[2520];
bool CYX_Hacked[2520];
bool ZJS_AC[2520];
bool CYX_AC[2520];
int ZJS_Problem_Score[2520];
int CYX_Problem_Score[2520];
map<string, int> Name_to_Id;
int Time_String_to_Int(string Time)
{
    int HH, MM, Minute;
    HH = (Time[0] - '0') * 10 + (Time[1] - '0');
    MM = (Time[3] - '0') * 10 + (Time[4] - '0');
    Minute = HH * 60 + MM;
    return Minute;
}
int Get_Int_Submission_Time(string Time)
{
    int Minute = Time_String_to_Int(Time);
    return Minute < INT_Start_Time ? Minute + 24 * 60 - INT_Start_Time : Minute - INT_Start_Time;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> k;
    for (int i = 1; i <= k; i++)
    {
        cin >> Problem_Name[i];
        Name_to_Id[Problem_Name[i]] = i;
    }
    cin >> Start_Time;
    INT_Start_Time = Time_String_to_Int(Start_Time);
    for (int i = 1; i <= n; i++)
    {
        cin >> ZJS_Submission[i].Time >> ZJS_Submission[i].Problem >> ZJS_Submission[i].Verdict;
        ZJS_Submission[i].Minute = Get_Int_Submission_Time(ZJS_Submission[i].Time);
    }
    for (int i = 1; i <= m; i++)
    {
        cin >> CYX_Submission[i].Time >> CYX_Submission[i].Problem >> CYX_Submission[i].Verdict;
        CYX_Submission[i].Minute = Get_Int_Submission_Time(CYX_Submission[i].Time);
    }
    for (int i = 1; i <= n; i++)
    {
        if (ZJS_Submission[i].Verdict == "Hacked")
        {
            if (!ZJS_Hacked[Name_to_Id[ZJS_Submission[i].Problem]])
            {
                CYX_Score += 100;
                ZJS_Hacked[Name_to_Id[ZJS_Submission[i].Problem]] = 1;
                ZJS_AC[Name_to_Id[ZJS_Submission[i].Problem]] = 0;
            }
        }
        else if (ZJS_Submission[i].Verdict == "CompileError")
        {
            continue;
        }
        else if (ZJS_Submission[i].Verdict.size() >= 23 && ZJS_Submission[i].Verdict.substr(0, 21) == "Wrongansweronpretest(")
        {
            if (ZJS_Submission[i].Verdict[21] == '1')
                continue;
            ZJS_WA[Name_to_Id[ZJS_Submission[i].Problem]]++;
        }
        else if (ZJS_Submission[i].Verdict == "PretestsPassed")
        {
            ZJS_AC[Name_to_Id[ZJS_Submission[i].Problem]] = 1;
            ZJS_Problem_Score[Name_to_Id[ZJS_Submission[i].Problem]] =
                max(
                    Name_to_Id[ZJS_Submission[i].Problem] * 150,

                    Name_to_Id[ZJS_Submission[i].Problem] * 500 -
                        ZJS_Submission[i].Minute * Name_to_Id[ZJS_Submission[i].Problem] * 2 -
                        ZJS_WA[Name_to_Id[ZJS_Submission[i].Problem]] * 50);
            ZJS_WA[Name_to_Id[ZJS_Submission[i].Problem]]++;
        }
    }
    for (int i = 1; i <= m; i++)
    {
        if (CYX_Submission[i].Verdict == "Hacked")
        {
            if (!CYX_Hacked[Name_to_Id[CYX_Submission[i].Problem]])
            {
                ZJS_Score += 100;
                CYX_Hacked[Name_to_Id[CYX_Submission[i].Problem]] = 1;
                CYX_AC[Name_to_Id[CYX_Submission[i].Problem]] = 0;
            }
        }
        else if (CYX_Submission[i].Verdict == "CompileError")
        {
            continue;
        }
        else if (CYX_Submission[i].Verdict.size() >= 23 && CYX_Submission[i].Verdict.substr(0, 21) == "Wrongansweronpretest(")
        {
            if (CYX_Submission[i].Verdict[21] == '1')
                continue;
            CYX_WA[Name_to_Id[CYX_Submission[i].Problem]]++;
        }
        else if (CYX_Submission[i].Verdict == "PretestsPassed")
        {
            CYX_AC[Name_to_Id[CYX_Submission[i].Problem]] = 1;
            CYX_Problem_Score[Name_to_Id[CYX_Submission[i].Problem]] =
                max(
                    Name_to_Id[CYX_Submission[i].Problem] * 150,

                    Name_to_Id[CYX_Submission[i].Problem] * 500 -
                        CYX_Submission[i].Minute * Name_to_Id[CYX_Submission[i].Problem] * 2 -
                        CYX_WA[Name_to_Id[CYX_Submission[i].Problem]] * 50);
            CYX_WA[Name_to_Id[CYX_Submission[i].Problem]]++;
        }
    }
    for (int i = 1; i <= k; i++)
    {
        if (ZJS_AC[i])
        {
            ZJS_Score += ZJS_Problem_Score[i];
        }
        if (CYX_AC[i])
        {
            CYX_Score += CYX_Problem_Score[i];
        }
    }
    cout << ZJS_Score << " " << CYX_Score << endl;
    if (CYX_Score > ZJS_Score)
    {
        cout << "cyx" << endl;
    }
    else if (ZJS_Score > CYX_Score)
    {
        cout << "zjs" << endl;
    }
    else
    {
        cout << "Wow!Same!" << endl;
    }
    return 0;
}

E. 绿茵战场


$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{B\textcolor{red}{yGones}\ and\ \tt{c\textcolor{red}{hufuzhe}}}$

Hint

Hint 1

首先,不考虑拜日教皇骗人的情况,我们如何准确查询?

Hint 2

当我们学会了不骗的人情况下准确查询后,如何知道拜日教皇有没有骗人呢?

Sol

之后的分析以及代码均由 $0$ 作为起始下标。

先考虑 Hint 1。

我们发现,$2^{31}-1=2,147,483,647$。

也就是说,$10^9$ 只需要大约 $30$ 个 bit 即可。

我们枚举每个 bit 表示的实际数字并查询。检查返回值与 $cnt$ 的大小关系。

如果返回值比 $cnt$ 小,说明这个 bit 被消除了,也就是原数的这个 bit 是 $1$

举个例子:$(10)_{10}=(1010)_2$。

$(10-2)_{10}=(8)_{10}=(1000)_2$。

这里我们消去第 $1$ 个 bit,也就是消去了 $2^1=2$。

$f(8)<f(10)$,第 $1$ 个 bit 确实是 $1$。

$(10-1)_{10}=(9)_{10}=(1001)_2$

这里我们消去第 $0$ 个 bit,也就是消去了 $2^0=1$。

$f(9)=f(10)$,第 $0$ 个 bit 不是 $1$。

所以退位什么只会导致 $1$ 个数相同或更多。感兴趣的话可以多找点例子。

综上,我们枚举 $0\sim 29$ 每一个 bit 代表的数 $x$ 并查询。如果得到的结果比 $cnt$ 小,那么 $ans\gets ans+x$,答案加上 $x$。

至此,Hint 1 的解决方法结束。

for(int i=0;i<30;i++)
{
    cout<<"? "<<(1<<max(i-1,0))<<'\n';
    fflush(stdout);
    int c;
    cin>>c;
    if(c==0)bit++,ans|=1<<i;
    if(bit==cnt)break;
}

如何处理 Hint 2 所提到的骗人情况呢?

我们发现题目中给了 $60$ 次的询问限制,而 Hint 1所提到的只有 $30$ 次询问。

我们发现,如果每次 ? 0 $f(n-x)$ 永远不变,一定是 $cnt$。也就是说,一旦返回不是 $1$,也就是不相等,那么一定骗人了。

因此,我们查询 $m$ 次 ? 0。因为骗人有周期性,所以想知道之后第 $i$ 条询问的回答是否骗人只需查看第 $i\bmod m$ 个询问的结果。

Code

代码

/*
Auther:  cyx2009
Luogu:   516346
QQ:      2176807108
*/
#include<bits/stdc++.h>
using namespace std;
bool f[50];//是否说谎 
int cnt,m;
int ans,bit; 
int main()
{
    cin>>cnt>>m; 
    for(int i=0;i<m;i++)
    {
        cout<<"? 0\n";
        fflush(stdout);
        int x;
        cin>>x;
        if(x!=1)f[i]=0;
        else f[i]=1;
    }
    for(int i=0;i<30;i++)
    {
        cout<<"? "<<(1<<max(i-1,0))<<'\n';
        fflush(stdout);
        int c;
        cin>>c;
        if(!f[i%m])c=(c+1)%3; 
        if(c==0)bit++,ans|=1<<i;
        if(bit==cnt)break;
        // cout<<bit<<"\n"; 
        // fflush(stdout);
    }
    cout<<"! "<<ans<<'\n';
    fflush(stdout);
    return 0;
}

Nothing

说句闲话,这题是一道大杂烩的原题。

主体部分(Hint 1)为 CF1780D,另一部分(Hint 2)为 CF1010B

在写下这段的时候,这两题在 CF 的评分均为 *1800,融合之后的这题,个人认为难度放到提高组也是不为过的。

至于为什么出交互,可能是受到了「信息与未来 2022」启发。小学生比赛都能出交互,普及组模拟赛不能出吗?

F. 数码方阵


$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{c\textcolor{red}{yx2009}}$

$\tt{J\textcolor{red}{Yawa}}$

Solution

首先,这里输入很奇葩。我们可以把它拆成一个序列,设序列长度为 $N=n\times m$。

假设 $A_{N-1}+2 \leq A_N$。可以证明,Xuan 总是会获胜。

  • 如果 Xuan 能够使得 $A_N<A_{N-1}$ 并让 Xiang 面对一个必输的局面,Xuan 就会赢。
  • 假设通过使 $a_N<a_{N-1}$ 能够获得所有必胜的局面。然后,如果我们 $a_N \gets a_{N-1}+1$,那么可以从该局面过渡到的所有局面都是必胜的。因此,将 $a_N \gets a_{N-1}+1$ 所得到的局面是必败的。所以,初始局面是必胜的。

接下来,考虑当给定一个满足 $a_N=a_{N-1}+1$ 的局面。根据上述证明,Xuan 和 Xiang 不应该给出最大两个元素差为 $2$ 或更大的局面。综上,我们可以假设 $a$ 的最大两个元素的差为 $1$。

然后,我们发现每一步操作都会将 $\max\{a\}$ 减少 $1$。

当 $\max\{a\}=N-1$ 时,该局面无法进行操作,因此可以根据输入中 $a_N$ 和 $N-1$ 的奇偶性差异来确定赢家。

复杂度为 $O(N)$。

但是输入不是有序,进行排序。复杂度为 $O(N \log N)$。

std 和这里的说法有些不同,std 写法更为简洁。

Code

代码

#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[300020];
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n * m; i++)
    {
        cin >> a[i];
    }
    sort(a + 1, a + n * m + 1);
    for (int i = 1; i <= n * m; i++)
    {
        a[i] -= i - 1;
    }
    if (a[n * m] == a[n * m - 1] && (a[n * m] % 2 == 0))
        puts("Xiang");
    else
        puts("Xuan");
    return 0;
}

最后修改:2023 年 07 月 11 日
v我50吃疯狂星期四