2024.11.21 2024辽宁省赛

Solved:11/13

Penalty:1224

Rank:3

Rank(vp):5

比赛链接


提交情况

没打过某两强校啊。。。


B. 比分幻术

签到,把输入的字符串反转输出。

#include<bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    string s;
    cin>>s;
    cout<<s[2]<<s[1]<<s[0]<<'\n';
}

J. 结课风云

签到,依次统计改分前后的通过人数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;

const int N=1005;
int n,a,b,c,d,x[N],y[N];

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>n>>a>>b>>c;
    int cnt1=0,cnt2=0;
    for(int i=1;i<=n;++i)cin>>x[i]>>y[i],cnt1+=x[i]+y[i]>=c;
    cin>>d;
    for(int i=1;i<=n;++i)x[i]=min(x[i]+d,a),cnt2+=x[i]+y[i]>=c;
    cout<<cnt2-cnt1<<'\n';
}

A. 爱上字典

不需要写哈希,直接用 set<string> 实现字符串查找,不会超时。

#include<bits/stdc++.h>
using namespace std;

int n;
string s,t;
set<string> ss;

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    getline(cin,s),s+=' ';
    cin>>n;
    for(int i=0;i<n;++i){
        cin>>t;
        ss.insert(t);
    }
    n=ss.size();
    int m=s.length();
    ll h1=0,h2=0;
    t="";
    for(int i=0;i<m;++i){
        char ch=s[i];
        if(ch>='A'&&ch<='Z')ch+=32;
        if(ch==' '){
            if(ss.find(t)==ss.end())ss.insert(t);
            t="";
        }
        if(ch>='a'&&ch<='z')t+=ch;
    }
    ss.erase("");
    cout<<ss.size()-n<<'\n';
}

L. 龙之研习

题意:闰年规则改为 \(4\times 100^p\) 年一闰,\(100^p\) 年不闰。求2024年开始第 \(k\) 个平年。

二分答案,问题转化为算 \(n\) 年之前有多少平年。对 \(p\) 从小到大枚举直到 \(100^p>n\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;

ll q,m;
ll chk(ll n){
    ll ans=0;
    for(ll r=1;r<=n;r*=100){
        ans+=n/r;
        ans-=n/(4*r);
    }
    return ans;
}
void solve(){
    cin>>m;
    ll l=m+2024,r=2*m+2024,mid,res;
    while(l<=r){
        mid=(l+r)>>1;
        if(chk(mid)-q>=m)res=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<res<<'\n';
}

int main(){
    q=chk(2024);
    ios::sync_with_stdio(0);cin.tie(0);
    int T;
    cin>>T;
    while(T--)solve();
}

C. 插排串联

题意:给一棵树,可以任意交换非根非叶子节点,问能否使得对每个非叶子节点,其子树中所有叶子节点权值之和小于等于自身权值。

题意等价于给每个非根非叶子节点分配一个大于等于某限制的权值。multiset存待取权值,然后贪心选取最小的满足限制的权值即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;

const int N=1e5+5;
int n,x;
ll a[N];
vector<int> e[N];
bool fl=1;
void adde(int x,int y){
    e[x].push_back(y);
}
multiset<ll> s;
void dfs(int u){
    if(!e[u].size())return;
    int sum=0;
    for(int v:e[u])dfs(v),sum+=a[v];
    if(!u)return;
    auto it=s.lower_bound(sum);
    if(it==s.end()){fl=0;return;}
    a[u]=sum;
    s.erase(it);
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>x>>a[i];
        adde(x,i);
    }
    ll sum=0;
    for(int i=1;i<=n;++i){
        if(e[i].size())s.insert(a[i]);
        else sum+=a[i];
    }
    if(sum>2200){cout<<"NO\n";return 0;}
    dfs(0);
    cout<<(fl?"YES":"NO")<<'\n';
}

E. 俄式简餐

题意:用 L 形和 I 形俄罗斯方块填充 \(n\times m\) 棋盘,给出方案。

格子数不能被 \(4\) 整除一定无解,另外 \(n=m=2\) 也无解。下面对剩余情况给出构造:

  • \(4|n\)\(4|m\):用 I 形填满即可;

  • \(n,m\) 均不能被 \(4\) 整除:容易构造出 \(2\times 6\)(或者 \(6\times 2\))的解,因此对前 \(6\) 行反复用 \(6\times 2\) 填,剩下用 I 填满即可。注意特判 \(n=2\)(此时没有前 \(6\) 行)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;

const int N=1e5+5;
int n,m;
void solve(){
    cin>>n>>m;
    if(n*m&3){cout<<"NO\n";return;}
    if(n==2&&m==2){cout<<"NO\n";return;}
    cout<<"YES\n";
    if(!(n&3)){
        for(int i=0;i<n;++i,cout<<'\n')
            for(int j=0;j<m;++j)
                cout<<(i>>2)*m+j+1<<' ';
    }
    else if(!(m&3)){
        for(int i=0;i<n;++i,cout<<'\n')
            for(int j=0;j<m;++j)
                cout<<i*(m>>2)+(j>>2)+1<<' ';
    }
    else{
        vector<vector<int>> a(n);
        for(int i=0;i<n;++i)a[i].resize(m);
        if(n==2){
            a[0][0]=a[0][1]=a[0][2]=a[1][0]=1;
            a[0][3]=a[0][4]=a[0][5]=a[1][5]=2;
            a[1][1]=a[1][2]=a[1][3]=a[1][4]=3;
            int cnt=3;
            for(int i=6;i<m;i+=4){
                a[0][i]=a[0][i+1]=a[0][i+2]=a[0][i+3]=++cnt;
                a[1][i]=a[1][i+1]=a[1][i+2]=a[1][i+3]=++cnt;
            }
        }
        else{
            int cnt=0;
            for(int i=0;i<m;i+=2){
                a[0][i]=a[1][i]=a[2][i]=a[0][i+1]=++cnt;
                a[3][i]=a[4][i]=a[5][i]=a[5][i+1]=++cnt;
                a[1][i+1]=a[2][i+1]=a[3][i+1]=a[4][i+1]=++cnt;
            }
            for(int i=6;i<n;i+=4)
                for(int j=0;j<m;++j)
                    a[i][j]=a[i+1][j]=a[i+2][j]=a[i+3][j]=++cnt;
        }
        for(int i=0;i<n;++i,cout<<'\n')
            for(int j=0;j<m;++j)
                cout<<a[i][j]<<' ';
    }
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int T;
    cin>>T;
    while(T--)solve();
}

G. 顾影自怜

题意:给一个序列,问有多少连续子列的最大值出现次数 \(\geq k\)

预处理每个值的所有出现位置(可以用vector数组)\(c_{i,j}\)
首先枚举最大值 \(x\) 及其第一次出现的位置 \(c_{x,i}\),则它第 \(k\) 次出现的位置为 \(c_{x,i+k-1}\)
二分子列左端点的最左位置 \(L\) 和右端点的最右位置 \(R\)。分别需要满足 \(\max(a[L...c_{x,i}])=x\)\(\max(a[c_{x,i+k-1}...R])=x\)。区间max可以用st表 \(O(n\log n)~O(1)\) 算。
总复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e6+5;
int n,k,a[N],st[20][N];
vector<int> pos[N];
int qry(int l,int r){
    int o=__lg(r-l+1);
    return max(st[o][l],st[o][r-(1<<o)+1]);
}

void solve(){
    cin>>n>>k;
    for(int i=1;i<=n;++i)cin>>a[i],pos[a[i]].push_back(i),st[0][i]=a[i];
    for(int i=1;1<<i<=n;++i)
        for(int j=1;j+(1<<i)-1<=n;++j)
            st[i][j]=max(st[i-1][j],st[i-1][j+(1<<i-1)]);
    ll ans=0;
    for(int i=1;i<=n;++i){
        for(int j=0;j+k-1<pos[i].size();++j){
            int lp=pos[i][j],rp=pos[i][j+k-1];
            if(qry(lp,rp)>i)continue;
            int l=j?pos[i][j-1]+1:1,r=lp,mid,L=lp,R=rp;
            while(l<=r){
                mid=(l+r)>>1;
                if(qry(mid,lp)==i)r=mid-1,L=mid;
                else l=mid+1;
            }
            l=rp,r=n;
            while(l<=r){
                mid=(l+r)>>1;
                if(qry(rp,mid)==i)l=mid+1,R=mid;
                else r=mid-1;
            }
            ans+=1ll*(lp-L+1)*(R-rp+1);
        }
    }
    cout<<ans<<'\n';
    for(int i=1;i<=n;++i)pos[i].clear();
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int T;
    cin>>T;
    while(T--)solve();
}

D. 都市叠高

题意:给一列二维平面上的点,将它们划分为若干区间,使得每个区间的点形成的凸包直径之和最大。

这是一道套着计算几何的皮的简单dp题(

点集凸包的直径一定是最远点对的距离。而在最优划分方案中,我们一定会把最远的两个点分到区间的左右端点(否则将这个区间拆成两段或者三段一定更优)。

因此有转移 \(f_i = \max_{j<i}\{f_{j-1} + d(a_i,a_j)\}\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=5005;
int n,x[N],y[N];
double f[N];
double dis(int i,int j){
    return sqrt(1.*(x[i]-x[j])*(x[i]-x[j])+1.*(y[i]-y[j])*(y[i]-y[j]));
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i)cin>>x[i]>>y[i];
    for(int i=1;i<=n;++i){
        f[i]=f[i-1];
        for(int j=1;j<i;++j)
            f[i]=max(f[i],f[j-1]+dis(i,j));
    }
    cout<<fixed<<setprecision(12)<<f[n]<<'\n';
}

K. 可重集合

题意:维护一个可重集 \(S\),支持插入和删除,并查询从集合中选取任意多个数相加能得到不同的和的数量。\(n\leq 5000,\sum(S)\leq 5\times 10^5\)

如果没有删除操作,直接用 bitset 维护即可做到 \(O(\frac{ns}w)\)

直接删除是不能用 bitset 做的(这等价于从 01 背包里删一个物品)。

每个物品的存在时间是一个区间,因此可用线段树分治将“删除一个物品”转化为“撤销加入一个物品”(后者直接把原来的 bitset 状态存下来即可)。

复杂度 \(O(\frac {ns\log n}w)\)。常数很小。

注:这个套路同样适用于一般的动态 01 背包问题。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=5005,M=5e5+5;
int n,m=5e5,op[N],x[N];
vector<int> tr[N*4],s[M];
bitset<M> now;

#define lc (x<<1)
#define rc (x<<1|1)
void insert(int x,int l,int r,int L,int R,int val){
    if(l==L&&r==R){
        tr[x].push_back(val);
        return;
    }
    int mid=(l+r)>>1;
    if(R<=mid)insert(lc,l,mid,L,R,val);
    else if(L>mid)insert(rc,mid+1,r,L,R,val);
    else insert(lc,l,mid,L,mid,val),insert(rc,mid+1,r,mid+1,R,val);
}
int ans[N];
void calc(int x,int l,int r){
    bitset<M> tmp=now;
    for(int val:tr[x])now|=now<<val;
    if(l==r){ans[l]=now.count()-1,now=tmp;return;}
    int mid=(l+r)>>1;
    calc(lc,l,mid),calc(rc,mid+1,r);
    now=tmp;
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i)cin>>op[i]>>x[i];
    for(int i=1;i<=n;++i){
        if(op[i]==1)s[x[i]].push_back(i);
        else insert(1,1,n,s[x[i]].back(),i-1,x[i]),s[x[i]].pop_back();
    }
    for(int i=1;i<=m;++i)
        for(int j:s[i])insert(1,1,n,j,n,i);
    now[0]=1,calc(1,1,n);
    for(int i=1;i<=n;++i)cout<<ans[i]<<'\n';
}

M. 盲盒谜题

题意:大模拟,略。

直接按题意暴力做就好了。复杂度 \(O(9^2k)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

const int N=1e5+5;
int n,m,k,t,a[N],ans[N];
vector<array<int,3>> lines={
    {1,2,9},{1,3,8},{1,4,7},{1,5,6},
    {2,3,4},{2,5,7},{4,6,9},{7,8,9}
};
int b[10];
bool empty(){
    for(int i=1;i<=9;++i)if(~b[i])return 0;
    return 1;
}
int first_empty(){
    for(int i=1;i<=9;++i)if(!~b[i])return i;
    return -1;
}
pii first_same(){
    for(int i=1;i<=9;++i)if(~b[i])
        for(int j=i+1;j<=9;++j)if(~b[j])
            if(b[i]==b[j])return {i,j};
    return {-1,-1};
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>n>>m>>k>>t;
    for(int i=1;i<=k;++i)cin>>a[i];
    memset(b,-1,sizeof(b));
    int l=1,cnt=n;
    bool end=0;
    while(!end){
        end=1;
        bool sp=0;
        for(int i=1;i<=9;++i)if(!~b[i]){
            if(l>min(k,cnt))break;
            end=0;
            if(a[l]==0){
                ++l,++cnt,sp=1;
                break;
            }
            else{
                b[i]=a[l];
                if(a[l]==t)++cnt;
                ++l;
            }
        }
        if(sp)continue;
        if(!~first_empty()&&first_same()==pii(-1,-1)){
            end=0,b[1]=-1;
        }
        sp=0;
        for(int i=0;i<8;++i)if(~b[lines[i][0]]&&b[lines[i][0]]==b[lines[i][1]]&&b[lines[i][1]]==b[lines[i][2]]){
            end=0;
            if(lines[i][0]>1)b[lines[i][0]]=-1;
            else sp=1;
            b[lines[i][1]]=b[lines[i][2]]=-1;
            cnt+=5;
        }
        pii t;
        while((t=first_same())!=pii(-1,-1)){
            end=0;
            if(t.first>1)b[t.first]=-1;
            else sp=1;
            b[t.second]=-1;
            ++cnt;
        }
        if(sp)b[1]=-1;
        if(end)break;
        if(empty())cnt+=10;
    }
    for(int i=1;i<=min(cnt,k);++i)++ans[a[i]];
    for(int i=0;i<=m;++i)cout<<ans[i]<<' ';
    cout<<'\n';
    if(k<cnt)cout<<"Unhappy! "<<cnt-k<<'\n';
}

I. 野兽节拍

题意:给一个字符串 \(S\),你可以任选一个长度为3的模式串 \(T\) 并不断消除 \(S\) 中出现的模式串直到无法消除为止。求最多的次数以及此时选择的 \(T\)

注意到所有模式串的消除次数之和是 \(O(n)\) 级别的,因此只需快速维护消除过程即可。

消除有两种情况,一是本身就有模式串作为子串,二是之前的消除产生了连锁反应。连锁反应只可能在上次消除的周围三个位置产生。

预处理每个模式串初始的出现位置,用链表维护当前的字符串。维护一个匹配指针,每次消除更新链表的同时枚举当前指针后面的三个位置,如果没找到模式串就说明这个位置的连锁反应已经结束了,直接把匹配指针移到下一个初始位置。

总复杂度 \(O(\sum ans(T))\)。事实上(官方题解)可以证明 \(\sum ans(T)\leq 20n\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

const int N=1e6+5;
int n,pre[N],nxt[N];
vector<pii> buf;
vector<int> pos[26][26][26];
void ins(int x,int y){
    pre[x]=y,nxt[x]=nxt[y];
    pre[nxt[y]]=x,nxt[y]=x;
}
void del(int x){
    buf.push_back(pii(x,pre[x]));
    pre[nxt[x]]=pre[x];
    nxt[pre[x]]=nxt[x];
}
string a;
int ans;
vector<int> anss;
int calc(int x,int y,int z){
    int i=pos[x][y][z][0],j=0,m=pos[x][y][z].size(),res=0;
    while(j<m){
        bool fl=0;
        for(int k=0;k<3&&i<=n;++k){
            if(pre[i]>0&&pre[pre[i]]>0&&a[i]==z&&a[pre[i]]==y&&a[pre[pre[i]]]==x){
                fl=1,i=nxt[i],++res;
                del(pre[i]),del(pre[i]),del(pre[i]);
                break;
            }
            i=nxt[i];
        }
        if(!fl){
            while(j<m&&pos[x][y][z][j]<i)++j;
            if(j<m)i=pos[x][y][z][j];
        }
    }
    for(int i=buf.size()-1;i>=0;--i)ins(buf[i].first,buf[i].second);
    buf.clear();
    return res;
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>n>>a,a=" "+a;
    for(int i=1;i<=n;++i)a[i]-='a';
    for(int i=0;i<=n+1;++i)pre[i]=i-1,nxt[i]=i+1;
    for(int i=3;i<=n;++i)pos[a[i-2]][a[i-1]][a[i]].push_back(i);
    
    for(int i=0;i<26;++i)
        for(int j=0;j<26;++j)
            for(int k=0;k<26;++k)if(pos[i][j][k].size()){
                int t=calc(i,j,k);
                if(t>ans)ans=t,anss={i,j,k};
            }
    cout<<ans<<'\n'<<char(anss[0]+'a')<<char(anss[1]+'a')<<char(anss[2]+'a')<<'\n';
}
posted @ 2024-11-22 02:53  EssnSlaryt  阅读(15)  评论(0编辑  收藏  举报