1-stl介绍_题解
作者:互联网
1-stl介绍_题解
2022/7/4-ACM暑期集训
A,最大或位_位运算贪心
题目大意
中文题自己看
思路和代码
要xy的或值最大则尽量让每一位或的结果都是1。考虑将y固定在r,x从l一点点变大。所以我们要让x在不大于r的情况下尽可能大。从大往小考虑二进制位。一旦出现“1<->0”的情况就表示后面可以全1(因为x不可能比y大了)。
void solve(){
ll l , r ;
cin >> l >> r ;
ll ans = 0 ;
drep(i , 0 , 63){
if(r >> i & 1){
ans |= (1ll << i) ;
if(!(l >> i & 1)){
ans += (1ll << i) - 1 ;
break ;
}
}
}
cout << ans << "\n" ;
}//code_by_tyrii
当然,这题放在STL专题里是为了介绍bitset的,但是bitset我感觉没什么用,都可以用位运算解决。
下面给出bitset的做法
void solve(){
ll l , r ;
cin >> l >> r ;
bitset<64> a(l) , b(r) ;
int p = 63 ;
while(p >= 0 && a[p] == b[p]) p -- ;
while(p >= 0) a[p -- ] = 1 ;
ll ans = 0 ;
rep(i , 0 , 63)
ans |= (1ll << i) * a[i] ;
cout << ans << '\n' ;
}//code_by_tyrii
B,水果_二维map
题目大意
中文题目自己看
思路和代码
hdu老题,注意格式。
是一个二维的矩阵,xy轴分别是产地和品种。这样直接就可以得到数值。所以就做一个二维的map即可。要注意的是,map是一棵树,二维的map就是树的每个节点都是一棵树。但是我们用的时候直接简单理解成二维的矩阵即可。
void solve(){
cin >> n ;
map<string , map<string , int> > mp ;
rep(i , 1 , n){
string s , t ;
int num ;
cin >> s >> t >> num ;
mp[t][s] += num ;
}
map<string , map<string , int> >:: iterator it ;
for(it = mp.begin() ; it != mp.end() ; it ++ ){
cout << (*it).fi << "\n" ;
map<string , int>::iterator it2 ;
for(it2 = ((*it).se).begin() ; it2 != ((*it).se).end() ; it2 ++ ){
cout << " |----" << (*it2).fi << "(" << (*it2).se << ")\n" ;
}
}
}//code_by_tyrii
C,Bombing_map+mset
题目大意
在一个二维平面上有n个敌军基地,现在要轰炸他们。每次可以轰炸所有水平或者竖直的敌人。问每次轰炸会摧毁几个敌军基地。(已经被摧毁的基地不能再被摧毁)
思路和代码
朴素做法直接开一个二维地图一点点炸,不过这样时空复杂度都会炸。
考虑横纵坐标分开考虑(坐标范围达到1e9基本上都是这样的做法)。对每一行每一列分别维护一个multiset表示每行中的列坐标和每列中的行坐标。
这样的话相当于对整个地图进行了离散化所以降低了空间复杂度。并且对点的删除操作可以达到O(n(log(logn)))。
int n , m , k ;
void solve(){
map<int , multiset<int> > mp[2] ;
rep(i , 1 , n){
int x , y ; cin >> x >> y ;
mp[0][x].insert(y) ;
mp[1][y].insert(x) ;
}
while(m -- ){
int dir , k ; cin >> dir >> k ;
cout << mp[dir][k].size() << "\n" ;
for(auto it : mp[dir][k]){
//(k , it)
mp[dir^1][it].erase(k) ;
}
mp[dir][k].clear() ;
}
cout << "\n" ;
}//code_by_tyrii
int main(){
ios::sync_with_stdio(false) ;
// cin.tie(0) ; cout.tie(0) ;
ll T ;
while(1){
cin >> n >> m ;
if(!n && !m) break ;
solve() ;
}
}
D,人见人爱A-B_set
题目大意
中自看
思路和代码
基础set的使用
void solve(){
while(1){
cin >> n >> m ;
if(!n && !m) return ;
set<int> st ;
rep(i , 1 , n){
int tmp ; cin >> tmp ;
st.insert(tmp) ;
}
rep(i , 1 , m){
int tmp ; cin >> tmp ;
if(st.size())
st.erase(tmp) ;
}
if(!st.size()) cout << "NULL" ;
else for(auto it : st)
cout << it << " " ; cout << "\n" ;
}
}//code_by_tyrii
E,Column Swapping_set
题目大意
给二维数组,现在可以交换任意两列,问是否可能使得每一行都非降序
思路和代码
找到每一行里需要被交换的元素,将其横坐标丢到set里面,由于set的插入是自己去重的所以比较方便。
void solve(){
cin >> n >> m ;
vct<vct<int> > a(n + 1 , vct<int>(m + 1 , 0)) ;
rep(i , 1 , n)
rep(j , 1 , m)
cin >> a[i][j] ;
auto b = a ;
rep(i , 1 , n)
sort(b[i].begin() + 1 , b[i].end()) ;
set<int> st ;
rep(i , 1 , n)
rep(j , 1 , m)
if(a[i][j] != b[i][j]) st.insert(j) ;
if(st.size() > 2) cout << "-1\n" ;
else if(!st.size()) cout << "1 1\n" ;
else{
int l = *st.begin() , r = *st.rbegin() ;
rep(i , 1 , n){
swap(a[i][l] , a[i][r]) ;
rep(j , 2 , m)
if(a[i][j - 1] > a[i][j]){
cout << "-1\n" ; return ;
}
}
cout << l << " " << r << "\n" ;
}
}//code_by_tyrii
F,看病要排队_PQ重载运算符
题目大意
中自看
思路和代码
每个医生做一个优先队列,重载小于号之后简单的入队出队即可
struct Node{
int num , imp ;
friend bool operator <( const Node &u , const Node &v){
if(u.imp == v.imp) return u.num > v.num ;
return u.imp < v.imp ;
}
};
int n ;
void solve(){
// cin >> n ;
priority_queue<Node> q[4] ;
int cnt = 0 ;
while(n -- ){
string op ; cin >> op ;
int doc , x ;
cin >> doc ;
if(op == "IN"){
cin >> x ;
Node tmp = { ++ cnt , x} ;
q[doc].push(tmp) ;
}else{
if(q[doc].size()){
cout << q[doc].top().num << "\n" ;
q[doc].pop() ;
}else cout << "EMPTY\n" ;
}
}
}//code_by_tyrii
int main(){
ios::sync_with_stdio(false) ;
while(cin >> n)
solve() ;
}
G,awoo's Favorite Problem_PQ
题目大意
给字符串s和t。现有两种操作,操作1使“ab”变成“ba”,操作2使“bc”变成“cb”。问经过若干次操作后s有无可能变成t。
思路和代码
将两个字符串一位一位进行匹配。然后就有四种情况:
- s_i==t_i 则匹配
- s_i == 'a' and t_i == 'b' 则表明i位置的b需要从[i,n]中的某个位置(假设是j)移动而来。考虑要从j一路移到i,则表明[i,j]中没有字符c(不然就断掉了)。而可以贪心的想到,j位置就是[i,n]区间中字符b的第一次出现位置。
- s_i == 'b' and t_i == 'c' 和上一种情况类似。
- else
那么考虑如何编码:
做三个set,表示abc三种字符在[i,n]区间中的下标序列。然后两个字符串一位一位的对齐匹配,出现不匹配情况就尝试去将ti移动到si位置。总体是一个模拟的思路。
string solve2(){
cin >> n ;
string s , t ;
cin >> s >> t ;
s = " " + s ;
t = " " + t ;
set<ll> idx[3] ;
rep(i , 1 , n)
idx[s[i] - 'a'].insert(i) ;
//[i,n]的abc三种字母的升序下标序列
rep(i , 1 , n){
// cout << i << "=>" << s << "\n" ;
if(s[i] == t[i]){//直接匹配
idx[s[i] - 'a'].erase(i) ;
continue ;
}
if(s[i] == 'a' && t[i] == 'b'){
//aaab 后面的b移到i位置
//baaa
if(!idx[1].size()) return "NO\n" ;//没有b可以换到i位置
int posb = *idx[1].begin() ;
int posc = *idx[2].begin() ;
if(idx[2].size() && posc < posb) return "NO\n" ;
idx[0].erase(i) ;
idx[1].erase(posb) ;
idx[0].insert(posb) ;
swap(s[i] , s[posb]) ;
// cout << i << " <- " << posb << "\n" ;
continue ;
}
if(s[i] == 'b' && t[i] == 'c'){
//bbbc
//cbbb
if(!idx[2].size()) return "NO\n" ;
int posc = *idx[2].begin() ;
int posa = *idx[0].begin() ;
if(idx[0].size() && posa < posc) return "NO\n" ;
idx[1].erase(i) ;
idx[2].erase(posc) ;
idx[1].insert(posc) ;
swap(s[i] , s[posc]) ;
continue ;
}
return "NO\n" ;
}
return "YES\n" ;
}
接下来是堆的解法:
仔细思索一下整个解题步骤:
遇到不匹配的时候尝试从后面换一个匹配的字符过来。而这个交换就是在字符的下标序列里面插入一个新的点。而每次交换的时候,换过来的字符肯定是所有同种字符里的第一个。就是每次取头,再插入。所以优先队列完美胜任。要注意在交换的时候对堆的维护,细节写在代码里了。
string solve3(){
string s , t ;
cin >> n >> s >> t ;
s = " " + s ;
t = " " + t ;
priority_queue<ll , vct<ll> , greater<ll> > idx[3] ;
rep(i , 1 , n) idx[s[i] - 'a'].push(i) ;
rep(i , 1 , n){
// cout << i << " " << s << "\n" ;
if(s[i] == t[i]){
// cout << idx[s[i] - 'a'].top() << "\n" ;
idx[s[i] - 'a'].pop() ;
continue ;
}
if(s[i] == 'a' && t[i] == 'b'){
if(!idx[1].size()) return "NO\n" ;//后面没有b可以交换到第i位
int posb = idx[1].top() ;
if(idx[2].size()){
int posc = idx[2].top() ;
if(posc < posb) return "NO\n" ;//中间被c截断了,b移不到i位置
}
// cout << idx[s[i] - 'a'].top() << "\n" ;
idx[s[i] - 'a'].pop() ;
swap(s[i] , s[posb]) ;//交换
idx[1].pop() ;
idx[0].push(posb) ;
continue ;
}
if(s[i] == 'b' && t[i] == 'c'){
if(!idx[2].size()) return "NO\n" ;//后面没有c可以交换到第i位
int posc = idx[2].top() ;
if(idx[0].size()){
int posa = idx[0].top() ;
if(posa < posc) return "NO\n" ;//中间被a截断了,c移不到i位置
}
// cout << idx[s[i] - 'a'].top() << "\n" ;
idx[s[i] - 'a'].pop() ;
swap(s[i] , s[posc]) ;//交换
idx[2].pop() ;
idx[1].push(posc) ;
continue ;
}
return "NO\n" ;
}
return "YES\n" ;
}
H,Alice and the Cake_思维-堆
题目大意:
一个蛋糕每次可以切一半,如12切成6+6,13切成6+7。现在给一堆无序的数,问是否可能由一个蛋糕切割得来。
思路和代码:
模拟切蛋糕的过程,并且每次切完贪心的去比较切出来的最大蛋糕和给出的最大蛋糕是否匹配。若不匹配就再去切即可。
string solve2(){
int n ;
cin >> n ;
vct<ll> a(n + 1 , 0) ;
priority_queue<ll , vct<ll> , less<ll> > now , q ;
rep(i , 1 , n) cin >> a[i] , q.push(a[i]) ;
ll sum = accumulate(a.begin() + 1 , a.end() , 0LL) ;
now.push(sum) ;
ll cnt = 0 ;
while(q.size() && now.size()){
ll ma = remain.top() ;
now.pop() ;
// cout << ma << " " << q.top() << "\n";
if(ma == q.top()){
q.pop() ;
}else{
// cout << cnt + 1 << "<\n" ;
if(++ cnt > n - 1) return "NO\n" ;
now.push(ma / 2) ;
now.push(ma / 2 + (ma % 2 ? 1 : 0)) ;
}
}
return "YES\n" ;
}
标签:set,cout,stl,题解,rep,cin,介绍,int,solve 来源: https://www.cnblogs.com/tyriis/p/16442911.html