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';
}
總結
這場的,銅牌題,還是看思維多一些,而且題的梯度安排比較接近,做的很舒適