The 2022 ICPC Asia Xian Regional Contest 前六題

lulaalu發表於2024-10-23

The 2022 ICPC Asia Xian Regional Contest

簽到題題解 CFJ

J. Strange Sum

易證最多隻能選兩個,從大到小排序後 \(\max(0, a_1) + \max(0, a_2)\) 即為所求。

void solve(){
    cin>>n;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a.begin()+1,a.end());
    ll ans=a[n]+a[n-1];
    ans=max(ans,a[n]);
    if(ans<0)cout<<0;
    else cout<<ans;
}

C. Clone Ranran

先出題再克隆一定不優,所以列舉克隆幾次,再出題,時間複雜度 \(\log n\)

ll f[32];
void pre(){
    f[0] = 1;
    for(int i=1;i<32;i++){
        f[i]=f[i-1]*2;
    }
}

void solve(){
    ll a,b,c;
    cin >> a >> b >> c;
    ll ans = 1e18;
    for(int i=0;i<32;i++){
        ll cnt = f[i];
        ll tans = ((c+cnt-1)/cnt)*b+i*a;
        ans = min(ans,tans);
    }
    cout << ans << '\n';
}

F Hotel

主要注意一下同一個房間的同性別的也可以住倆房間

string s[1010];
void solve(){
    int n,c1,c2;
    cin >> n >> c1 >> c2;
    for(int i=0;i<n;i++){
        cin >> s[i];
    }    
    if(c1 * 2 <= c2){
        cout << 3 * n * c1;
        return;
    }
    int ans = 0;
    for(int i=0;i<n;i++){
        if(s[i][0]==s[i][1] || s[i][0]==s[i][2] || s[i][1] == s[i][2]){
            ans += min(c1,c2)+c2;
        }else{
            ans += 3*min(c1,c2);
        }
    }
    cout<<ans;
}

G. Perfect Word

題意

給若干串,要求構造一個串,這個串的所有子串都在給的串中出現

思路

我感覺有點巧妙

先按長度排好序,每次列舉給的串 \(s\),如果\(s[0\dots n-2],s[1\dots n-1]\) 都出現了,那麼把\(s\) 加入 \(set\)

注意,如果 \(s[0\dots n-2]\)\(set\) 中出現了,說明對應的-1長度的兩個子串也出現

也就是說對 \(s\) 長度-1的串的判定實際上是對s所有子串都判定了

給定的所有串長度是 \(1e5\) ,甚至不需要字串雜湊之類的,直接就裝得下

程式碼

#define ll long long
#define inf INT_MAX
#define INF LONG_LONG_MAX
#define endl '\n'

const int N = 1e5 + 5;

string s[N];
set<string>hsh; // 一開始寫雜湊的,寫完調了一下被隊友發現根本不用多此一舉

void solve(){
    int n;
    cin >> n;
    for(int i=0;i<n;i++){
        cin >> s[i];
    }

    sort(s,s+n,[](string &s1,string &s2){
        return s1.length() < s2.length();
    });

    int ans = 1;
    for(int i = 0; i < n; i++){
        int len = s[i].length();
        if(len == 1)hsh.insert(s[i]);
        else{
            if(hsh.find(s[i].substr(0,len-1)) != hsh.end() && hsh.find(s[i].substr(1,len-1)) != hsh.end()){
                ans = s[i].length();
                hsh.insert(s[i]);
            }
        }
    }
    cout << ans << endl;
}

L. Tree

題意

給一顆樹,分成若干個點集,要求每個點集內任意兩個點滿足以下兩個要求之一

  • 對於所有 \(u, v\in S\)\(u\neq v\), 要麼u在v的子樹中,要麼v在u的子樹內
  • 對於所有 \(u, v\in S\)\(u\neq v\), 同時滿足uv不在相互的子樹中

思路

分析一下就是,要麼點集內所有的點組成一條鏈,要麼是點集內沒有任何兩個點的LCA同樣在點集內

從一開始的考慮要麼是葉子結點的數量,要麼是層數

到把鏈縮成點後的要麼是葉子結點要麼是層數

到最後的,每次把所有葉子結點劃分為一個點集,然後從圖中刪除

大致是這樣,具體實現還得看隊友啊

程式碼

#define ll long long
const int N = 1000005; 
int f[N], in[N]; 

void solve(){
    int n; 
    cin>>n; 
    for(int i = 1;i<=n;++i)
        in[i] = 0;
    for(int i = 2;i<=n;++i){
        cin>>f[i]; 
        ++in[f[i]]; 
    }
    vector<int> vec1, vec2, *cur = &vec1, *next = &vec2; 
    for(int i = 1;i<=n;++i){
        if(!in[i])
        cur->push_back(i); 
    }
    int ans = cur->size(); 
    int layer = 0; 
    do{
        ++layer; 
        for(auto iter:*cur){
            --in[f[iter]]; 
            if(!in[f[iter]] && f[iter])
                next->push_back(f[iter]); 
        }
        swap(cur, next);  
        ans = min(ans,(int)(layer+cur->size())); 
        next->clear(); 
    }while(!cur->empty()); 
    cout<<ans<<endl; 

}
int t=1;
int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

E. Find Maximum

題意

\(f(x) = \begin{cases} 1 & (x = 0) \\ f(\frac{x}{3}) + 1 & (x > 0\land x\bmod3 = 0) \\ f(x - 1) + 1 & (x > 0\land x\bmod 3\neq 0) \end{cases}\)

計算\(\max_{x = l} ^ r f(x)\).

\(1\leq l\leq r\leq 10 ^ {18}\)

思路

發現 f(n) 是 n 在三進位制下的數碼和加上數位個數

那麼取R的三進位制,然後列舉每一位,當前位-1,當前位後面的全變為2

相當於考慮儘可能多的2,最高就80位

隊友太給力啦,最後一小時調出來了

程式碼

#define ll long long
ll l,r;
ll ksm(ll x,ll y){
    ll res=1;
    while (y>0)
    {
        if(y&1)res=res*x;
        x=x*x;
        y=y/2;
    }
    return res;
}
ll f(ll x){
    if(x == 0)return 1;
    if(x % 3 == 0)return f(x/3)+1;
    return f(x-1)+1;
}
vector<ll> three(ll x){
    vector<ll>y;
    while (x>0)
    {
        y.push_back(x%3);
        x/=3;
    }
    return y;
}
ll ten(vector<ll> x){
    ll y=0;
    for(int i=0;i<x.size();i++){
        y+=ksm(3,i)*x[i];
    }
    return y;
}
void solve(){
    cin>>l>>r;
    vector<ll>rr=three(r);
    ll ans=0;
    ans=max(f(l),f(r));
    for(int i=rr.size()-1;i>=0;i--){
        vector<ll>temp=rr;
        if(rr[i]==1&&i!=rr.size()-1){
            temp[i]=0;
            for(int j=i-1;j>=0;j--){
                temp[j]=2;
            }
            if(ten(temp)>=l){
                ans=max(ans,f(ten(temp)));
                break;   
            }
        }
        else if(rr[i]==2){
            temp[i]=1;
            for(int j=i-1;j>=0;j--){
                temp[j]=2;
            }       
            if(ten(temp)>=l){
                ans=max(ans,f(ten(temp)));
                break;   
            }   
        }
    }
    for(int i=0;i<rr.size()-1;i++)rr[i]=2;
    rr[rr.size()-1]=0;
    if(ten(rr)>=l)ans=max(ans,f(ten(rr)));
    cout<<ans<<'\n';
}

總結

這場的,銅牌題,還是看思維多一些,而且題的梯度安排比較接近,做的很舒適

相關文章