AtCoder Beginner Contest 173 題解
A - Payment
首先我們可以把所有不用找零的部分都付掉,這樣就只剩下了\(A \mod 1000\)這樣一個“\(A\)除以\(1000\)的余數部分”。
然后我們再來用\(1000\)減去它,就是要找的零錢,但是假如剛好余數為\(0\)就會出鍋,所以再要特判一下,程序這里直接寫進公式里了:
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
cout<<(1000-n%1000)%1000<<endl;
return 0;
}
B - Judge Status Summary
這波啊,這波是小學數數(霧)。這就是普通的計數啊,我們可以使用C++的STL中的一位:map
。
這里講了map
的使用方法:OI-wiki
然后你建立一個字符串對應到整數的map
,然后每次讀入一個字符串都把對應值加一就行。然后輸出每個狀態對應的值就好。
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
map<string,int> mp;
string s;
int n;
cin>>n;
while(n--){
cin>>s;
mp[s]++;
}
cout<<"AC x "<<mp["AC"]<<'\n';
cout<<"WA x "<<mp["WA"]<<'\n';
cout<<"TLE x "<<mp["TLE"]<<'\n';
cout<<"RE x "<<mp["RE"]<<'\n';
return 0;
}
C - H and V
很顯然數據這么小,我們可以枚舉哪些列需要塗色,然后再計算沒有塗過的黑色塊就好了。
這里你需要學習狀態壓縮來更好地枚舉塗色的列的集合,你可以把集合壓縮成一個整數,當中對應的位為\(1\)表示這一行/列塗色了,否則沒有塗。這里來學習位運算和狀態壓縮(狀壓DP順便也看看吧,會有用的)。
我這里設置了第\(1\)到\(H\)的二進制位表示對應的行的狀態,同理第\(H+1\)到\(W\)位表示對應的列的狀態。
#include<bits/stdc++.h>
using namespace std;
int h,w,k;
char g[10][10];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>h>>w>>k;
for(int i=0;i<h;i++){
cin>>g[i];
}
int ans=0;
for(int s=0;s<1<<h+w;s++){//先執行+再執行<<,A<<B表示A*2^B
int c=0;
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
c+=!(s>>i&1)&&!(s>>h>>j&1)&&g[i][j]=='#';//先執行>>再執行&,A>>B&1表示A的第B位,值是1或0
}
}
ans+=c==k;
}
cout<<ans<<endl;
return 0;
}
D - Chat in a Circle
首先我們先把\(A\)從大到小排列一下,並從前往后地讓對應位置的人進圈。感性地想,讓權值高的人先進,就可以影響旁邊人的心情更好,可以證明是對的,但我不會qaq。
然后容易看出,除了第一個人只能影響第二個之外,其余的人都有兩個可以影響的空位(廢話,左右兩邊各一個,寫題解的人腦子有病吧)。然后就好了可以寫程序了(
#include<bits/stdc++.h>
using namespace std;
int n;
int a[200005];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
sort(a,a+n,[](const int &a,const int &b){
return a>b;
});
int tot=n-1;
long long ans=0;
for(int i=0;i<n;i++){
if(tot){
ans+=a[i];
tot--;
}
if(tot&&i){
ans+=a[i];
tot--;
}
}
cout<<ans<<endl;
return 0;
}
E - Multiplication 4
我們先判斷一下能不能弄出一個正的乘積啊,然后退而求其次看能不能弄出\(0\)來,最后再盡量弄一個絕對值小的負數乘積啊。
假如負數兩兩一對,加上一部分正數能夠湊到\(K\)個,就可以弄出正的乘積了。
先看正的乘積,我們把正數負數絕對值從大到小排序,然后兩兩一對乘起來。然后盡量取大的一對乘起來,最后假如\(K\)是奇數就添上一個單獨的正數就好了啊。
UPD: 這邊正的乘積需要先考慮\(K\)是奇數的情況並先添上一個正數,否則會被叉。
乘積為\(0\)的話墜簡單,輸出零就好了。
負數的乘積其實也很簡單,由於你已經通過先前的判斷證明了不能有正數或者零的乘積,那么只要貪心地取絕對值最小的那些正/負數乘起來就好了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll n,k,zero;
ll a[200005];
vector<ll> pos,neg;
void find_pos(){
sort(pos.begin(),pos.end(),[](const ll &a,const ll &b){
return a>b;
});
sort(neg.begin(),neg.end(),[](const ll &a,const ll &b){
return a<b;
});
ll res=k&1?pos.front():1;
ll pi=k&1,ni=0;
while(pi+1<pos.size()&&ni+1<neg.size()&&pi+ni+2<=k){
if(pos[pi]*pos[pi+1]>neg[ni]*neg[ni+1]){
res=res*pos[pi]%mod*pos[pi+1]%mod;
pi+=2;
}else{
res=res*neg[ni]%mod*neg[ni+1]%mod;
ni+=2;
}
}
while(pi+1<pos.size()&&pi+ni+2<=k){
res=res*pos[pi]%mod*pos[pi+1]%mod;
pi+=2;
}
while(ni+1<neg.size()&&pi+ni+2<=k){
res=res*neg[ni]%mod*neg[ni+1]%mod;
ni+=2;
}
cout<<(res+mod)%mod<<endl;
}
void find_neg(){
sort(a,a+n,[](const ll &a,const ll &b){
return abs(a)<abs(b);
});
ll res=1;
for(ll i=0;i<k;i++){
res=res*a[i]%mod;
}
cout<<(res+mod)%mod<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
for(ll i=0;i<n;i++){
cin>>a[i];
if(a[i]>0)pos.emplace_back(a[i]);
if(a[i]==0)zero++;
if(a[i]<0)neg.emplace_back(a[i]);
}
if(neg.size()-(neg.size()&1)+pos.size()>=k&&(k&1)<=pos.size()){
find_pos();
}else if(zero){
cout<<"0\n";
}else find_neg();
return 0;
}
F - Intervals on Tree
首先這是一棵樹,所以每一條邊都會合並左右結點從屬的兩個連通塊為一個。那么對於一條邊,對於所有包含它的\(S\)(定義同題目),它就會減少一個連通塊。
那么答案一開始就設成假如每個\(S\)中的點都是獨立的一個連通塊,一共有多少個連通塊,然后對於每一條邊,答案減去包含它的\(S\)的個數即可。
#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
long long ans=0;
for(int i=1;i<=n;i++){
ans+=(long long)(n-i+1)*i;
}
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
if(a>b)swap(a,b);
ans-=(long long)a*(n-b+1);
}
cout<<ans<<endl;
return 0;
}