AtCoder Beginner Contest 403

C - 403 Forbidden

对每个用户,先判断有无所有权限,若无,再判断有无单独查看Y的权限。

过程中用set/map维护即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;

void solve(){
    int n,m,q;
    cin>>n>>m>>q;

    vector<map<int,int>> a(n+10);
    map<int,int> mp;

    while(q--){
        int ch,x,y;
        cin>>ch;
        if(ch==3){
            cin>>x>>y;
            if(mp[x]==0 && a[x][y]==0){
                cout<<"No\n";
            }else{
                cout<<"Yes\n";
            }

        }else if(ch==2){
            cin>>x;
            mp[x]=1;
        }else{
            cin>>x>>y;
            a[x][y]=1;
        }
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    // cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}

D - Forbidden Difference

很典的题,很典的做法。

要删除一些数字,使得不存在 Bi-Bj == D

如果两个数的差值等于D,则这两个数对D取模的值一定相同。

所以,将所有数分成D组,分组依据就是对D取模的值。每组之间互不影响,讨论每组组内最少删除几个即可.

对于一组内的数,可以用dp来计算最少删除几个。

因为值域很小,所以不用真的把组分出来,只需要枚举值域即可。DP 很简单,看代码即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;

const int N=1e6+10;

void solve(){
    int n,k;
    cin>>n>>k;

    int mx=1e6;

    vector<int> cnt(1000010);

    for(int i=1;i<=n;i++){
        int val;
        cin>>val;
        cnt[val]++;
    }

    int ans=0;

    if(k==0){
        for(int i=0;i<=mx;i++){
            if(cnt[i]) ans+=cnt[i]-1;
        }
        cout<<ans<<endl;
        return;
    }

    for(int i=0;i<k;i++){
        array<int,2> dp{0,0};
        for(int j=i;j<=mx;j+=k){
            //1:删 0:不删
            array<int,2> tmp{0,0};
            tmp[0]=dp[1];
            tmp[1]=min(dp[0],dp[1])+cnt[j];
            dp=tmp;
        }
        ans+=min(dp[0],dp[1]);
    }
    cout<<ans<<endl;

}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    // cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}

E - Forbidden Prefix

字典树典题。

字符串分两种,X串和Y串。

需要维护几个数组,flag[i]表示,在字典树的第i个节点,有没有X串在P的位置结束。

当插入一个Y串时,如果Y串的路径上,已有flag[i]=1,则这个路径上存在一个前缀。

插入一个X串时,如果这个X串的终点,已有一些Y串经过,则这些Y串中,还在ans中的应该被减去。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;
const int N=5e5+10;

int ans,idx;

int tr[N][26];
bool flag[N];//第i个节点有无前缀存在
int st[N];// 第i个Y中的字符串还在不在答案里 0在1不在
vector<int> pos[N];
bool vis[N];
int id;

void insert1(string s){
    int p=0;//这个字符串最后到的节点
    for(auto ch:s){
        int val=ch-'a';
        if(!tr[p][val]){
            tr[p][val]=++idx;
        }
        p=tr[p][val];
    }
    flag[p]=1;

    for(auto x:pos[p]){
        //去掉所有经过p且还在答案里的Y串
        if(!vis[x]){
            ans--;
            vis[x]=1;
        }
    }
    pos[p].clear();
}

void insert2(string s){
    int p=0;
    id++;// 这个Y串的id
    ans++;

    for(auto ch:s){
        int val=ch-'a';
        if(!tr[p][val]){
            tr[p][val]=++idx;
        }
        p=tr[p][val];
        pos[p].push_back(id);
        if(!vis[id] && flag[p]){
            ans--;
            vis[id]=1;
            break;
        }
    }
}

void solve(){
    int q;
    cin>>q;

    while(q--){
        int t;
        string s;
        cin>>t>>s;
        
        if(t==1){
            insert1(s);
        }else{
            insert2(s);
        }
        cout<<ans<<endl;
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    // cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}

F - Shortest One Formula

当感觉完全没有思路的时候,想想dp

设表示数字i的最短字符串是f[i], 则f[i] 一定是从f[i-j]+f[j] , 和 f[i/j]*f[j]转移过来的

但是当考虑从乘法转移f[i/j] * f[j]时,还要考虑f[i/j]和f[j]最外层的运算符号。举例来说:

假设f[6]=f[2]*f[3], f[2]=1+1, f[3]=1+1+1。 此时不能直接乘起来,因为会变成1+1 * 1+1+1

显然是有问题的,因为此时f[2]或f[3]的最外层运算符是+号

所以还要加一个维度:

f[i][0]表示,结果是i,且最外层运算符是 * 号的表达式。

f[i][1]表示,结果是i,且最外层运算符是 + 号的表达式。

要注意转移的初始值和一些转移过程中的细节

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
using lll=__int128_t;
using ull=unsigned long long;
const ll inf=1e18;
const int mod =1e9+7;

string minstr(string &a,string &b){
    if(a.empty()) return b;
    if(b.empty()) return a;

    if(a.size()>b.size()) return b;
    return a;
}

void solve(){
    int n;
    cin>>n;

    vector<vector<string>> f(n+10,vector<string>(2));

    int k=1;

    f[1][0]="1";
    f[1][1]=string(10000,'m');

    for(int i=2;i<=n;i++){
        while(k<i){
            k=k*10+1;
        }
        if(k==i){
            f[i][0]=to_string(k);
        }

        //f[i][0]:最外层是*号连接的 f[i][1]:最外层是由+号连接的

         //1. 从加法转移
        for(int j=1;j<=i/2;j++){
            //f[j]+f[i-j];
            string tmp=minstr(f[j][0],f[j][1])+"+"+minstr(f[i-j][0],f[i-j][1]);
            if(tmp.size()<f[i][1].size() || f[i][1].size()==0){
                f[i][1]=tmp;
            }
        }

        //2. 从乘法转移
        for(int j=2;j*j<=i;j++){
            if(i%j) continue;
            //f[j]*f[i/j]
            string fj1="("+f[j][1]+")";
            string fij1="("+f[i/j][1]+")";
            string tmp=minstr(f[j][0],fj1)+"*"+minstr(f[i/j][0],fij1);
            if(tmp.size()<f[i][0].size() || f[i][0].size()==0){
                f[i][0]=tmp;
            }
        }
    }

    cout<<minstr(f[n][0],f[n][1]);
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    // cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}
posted @ 2025-05-01 18:30  LYET  阅读(44)  评论(0)    收藏  举报