一些思维题记(长期更新)

这篇博客包含了多个Codeforces编程题目(如Prime Subtraction, Kill 'Em All, Standard Free2play等)的解题思路,涉及数论、排序、模拟、字符串处理和数据结构(如线段树)等技巧。博主分享了如何处理不同问题,如DFS序的应用、区间维护和贪心策略等。" 121546286,5589275,剑指 Offer 30. 实现带有min函数的栈,"['数据结构', '算法', '栈操作', '最小元素']
摘要由CSDN通过智能技术生成

A. Prime Subtraction Codeforces 1238A

题意: 给出两个数 A , B A,B A,B,询问 ∣ A − B ∣ |A-B| AB是否是素数的倍数。
思路:
这题涉及一点小数论,每个数都能进行质因数分解(除了1),所以只要A-B不为1就输出YES,否则输出NO

B. Kill 'Em All 1238B

题意:
在坐标 x x x的地方扔炸弹,位于 x x x的地方的怪会直接去世,若不位于,则会被爆炸冲击波击退 k k k个单位

思路: 排序去重,从最大的 x x x开始扔炸弹即可。

C. Standard Free2play (模拟)

题意: 一个人位于 h h h的板子上,通过一些操作(拉动拉杆,使得 h 和 h − 1 h和h-1 hh1的踏板状态改变,如果原来是出来的,现在收回去),或者通过使用石头使得任意的踏板改变状态,达到高度 0 0 0,问最小用多少个石头。

思路: 如果直接模拟h (1e9)会超时,注意到告诉你踏板的状态只有n(1e5),于是就从这个角度入手。
看下一个踏板的高度和当前高度,如果说下一个踏板的高度不是 n o w H e i g h t − 1 nowHeight-1 nowHeight1,那么可以直接从 h h h高度跳到 H n x t − 1 H_{nxt}-1 Hnxt1,反之需要判断以下,具体看代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int arr[maxn];
int main()
{
    int q;  cin >> q;
    while(q--){
        int h,n;    cin >> h >> n;
        for(int i = 0; i < n; ++i){
            cin >> arr[i];
        }
        int now = h,ans = 0;
        for(int i = 1; i < n;){
            if(now<=2)  break;
            if(arr[i]==now-1){
                if(i+1 < n&&arr[i+1]==now-2){
                    i+=2;   now = now-2; continue;   // 到下一个
                }else{
                    ++ans,now = now-2,++i;
                }
            }else if(arr[i]!=now-1){
                now = arr[i]+1;
            }
        }
        cout << ans << endl;
    }
    return 0;
}

D. AB-string 1238D
去除掉 “AAAAB",“BBBBA” 类似的情况就可以了。

C. Registration system

思路:map记录即可。

#include <bits/stdc++.h>
using namespace std;
map<string,int> mp;
int main()
{
	int n;	cin >> n;
	for(int i = 0; i < n; ++i){
		string str;	cin >> str;
		if(!mp.count(str)){
			cout << "OK" << endl;
			mp[str]++;
		}else{
			cout << str << mp[str]++ << endl;
		}
	}
	return 0;
}

A. Two Substrings
题意:问字符串里有没有 A B AB AB, B A BA BA
思路:暴力乱搞

#include <bits/stdc++.h>
using namespace std;
map<string,int> mp;
int main()
{
	string str;	cin >> str;
	bool isok1 = false,isok2 = false;
	for(int i = 0,n = str.size(); i < n; ++i){
		if(i+1<n && str[i]=='A' && str[i+1]=='B'){
			for(int j = i+2; j < str.size(); ++j){
				if(j+1 < n&& str[j]=='B' && str[j+1]=='A'){
					cout << "YES" << endl;
					return 0;
				}
			}
		}
		else if(i+1<n && str[i]=='B' && str[i+1]=='A'){
			for(int j = i+2; j < str.size(); ++j){
				if(j+1 < n&& str[j]=='A' && str[j+1]=='B'){
					cout << "YES" << endl;
					return 0;
				}
			}			
		}
	}
	cout << "NO" << endl;
	return 0;
}

B. Minimum Ternary Strings

思路:可以发现1的位置是任意的,所以贪心的考虑即可

#include <bits/stdc++.h>
using namespace std;
int main()
{
    string str; cin >> str;
    string ans;
    int one = 0;
    for(int i = 0,n = str.size(); i < n; ++i){
        if(str[i]=='1') one++;
        else ans += str[i];
    }
    bool isprint = false;
    for(int i = 0,n = ans.size(); i < n; ++i){
        if(ans[i]=='0'){
            cout << ans[i];
        }else if(ans[i]=='2'){
            if(!isprint){
                for(int j = 0; j < one; ++j)    cout << '1';
                isprint = true;
            }
            cout << ans[i];
        }
    }
    if(!isprint){
        cout << string(one,'1');
    }
    cout << endl;
    return 0;
}

E. Military Problem

询问:(u,k) 从u开始第k个被dfs到的点是什么

思路: DFS序的应用

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
vector<int> edge[maxn];
int dfn[maxn],siz[maxn];  bool vis[maxn];
int cnt = 0;
int pos[maxn];
void dfs(int u,int fa){
    dfn[++cnt] = u;
    pos[u] = cnt;
    vis[u] = true;
    siz[u] = 1;
    for(int i = 0; i < edge[u].size(); ++i){
        if(!vis[edge[u][i]]&& edge[u][i]!=fa){
            dfs(edge[u][i],u);
            siz[u] += siz[edge[u][i]];
        }
    }
}
int main()
{
    int n,q;    cin >> n >> q;
    for(int i = 2,x; i <= n; ++i){
        cin >> x;   edge[x].push_back(i);
    }
    dfs(1,0);
    for(int i = 0; i < q; ++i){
        int u,k;    cin >> u >> k;
        if(k > siz[u]){cout << -1 << endl;}
        else cout << dfn[pos[u] + k - 1] << endl;
    }
    return 0;
}

1234D - Distinct Characters Queries (线段树维护区间内不同字母的二进制)
思路:因为最多26个字母,所以用一个26位的二进制数来表示区间有哪些字母,合并区间的时候用或操作就可以。(第一次知道线段树还能这么整)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
struct Tree{
    int bit;
}tree[maxn<<2];
inline int lson(int root){return root << 1;}
inline int rson(int root){return root << 1|1;}
inline void push_up(int root){
    tree[root].bit = tree[lson(root)].bit | tree[rson(root)].bit;
}
void BuildTree(int l,int r,int root,string str){
    if(l==r){
        tree[root].bit = 1 << (str[l-1]-'a');
 
       // cout << l <<' '<< tree[root].bit << endl;
        return;
    }
    int mid = l + r >> 1;
    BuildTree(l,mid,lson(root),str);
    BuildTree(mid+1,r,rson(root),str);
    push_up(root);
}
void update(int l,int r,int pos,int root,char c){
    if(l==r){
        tree[root].bit = 1 << (c - 'a');
        return;
    }
    int mid = l + r >> 1;
    if(pos <= mid)    update(l,mid,pos,lson(root),c);
    else update(mid+1,r,pos,rson(root),c);
    push_up(root);
}
int query(int l,int r,int ql,int qr,int root){
    if(ql <= l && r <= qr){
        return tree[root].bit;
    }
    int mid = l + r >>   1;
    int ans = 0;
 
 
    if(ql <= mid){
        ans |= query(l,mid,ql,qr,lson(root));
    }
    if(qr > mid){
        ans |= query(mid+1,r,ql,qr,rson(root));
    }
    return ans;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);	cout.tie(0);
    string str; cin >> str;
    BuildTree(1,str.size(),1,str);
    int n;  cin >> n;
    for(int i = 0; i < n; ++i){
        int op,l,r,pos; char c;
        cin >> op;
 
        if(op==1){
            cin >> pos >> c;
            update(1,str.size(),pos,1,c);
        }else{
            cin >> l >> r;
            int cnt = query(1,str.size(),l,r,1),ans = 0;
            // cout << cnt << endl;
            for(int i = 0; i < 26; ++i){
                if(cnt & 1)
                    ++ans;
				cnt >>=1;
            }
            cout << ans << '\n';
        }
    }
 
    return 0;
}

1219C - Periodic integer number 字符串构造(多种情况考虑)
思路:对于这种构造模拟题最好还是在纸上把情况都列出来,直接敲容易遗漏…
具体看代码,主要是区分 l e n m o d    n = = 0 len \mod n==0 lenmodn==0 l e n m o d    n ! = 0 len\mod n !=0 lenmodn!=0的情况

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int n;	cin >> n;
	string str;	cin >> str;
	int len = str.size();
	if(len%n!=0){
		string temp;
		temp += '1';
		temp += string(n-1,'0');
		for(int i = 0,x = int(ceil(len*1.0/n)); i < x; ++i){
			cout << temp;
		}
		cout << endl;
	}else{
		string temp = str.substr(0,n);
		bool isbig = false;
		for(int i = n; i < len; i+=n){
			for(int j = 0; j < n; ++j){
				if(str[i+j] < temp[j]){
					isbig = true;
				}else if(str[i+j]>temp[j])  break;
			}
		}
		int jin = 0;
		if(!isbig){ // 就说明是循环节类型
			temp[n-1] = temp[n-1] + 1;
			jin = (temp[n-1] - '0')/10;
			temp[n-1] = (temp[n-1] - '0')%10 + '0';
			if(jin){
				for(int i = n-2; i >= 0; --i){
					temp[i] = temp[i] + jin;
					jin = (temp[i] - '0')/10;
					temp[i] = (temp[i] - '0')%10 + '0';
					if(jin==0)	break;
				}
			}
		}
		//cout << temp << endl;
 
		for(int i = 0,x = len/n+jin; i < x; ++i){
			if(jin)	cout << '1' << temp.substr(1,n-1);
			else cout << temp;
		}
 
		cout << endl;
	}
	return 0;
}

1218F Workout plan (简单的贪心)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e5+5;
ll arr[maxn],money[maxn];
set<ll> st;
int main()
{
    ll n,k;    cin >> n >> k;
    for(ll i = 0; i < n; ++i)  cin >> arr[i];
    ll A;  cin >> A;
    for(ll i = 0; i < n; ++i)  cin >> money[i];
    ll ans = 0;    bool isok = true;
    for(ll i = 0; i < n; ++i){
        if(k >= arr[i]){
            st.insert(money[i]);
        }else{
            st.insert(money[i]);
            while(k < arr[i] && !st.empty()){
                k += A;
                ans += *st.begin();
                st.erase(st.begin());
            }
            if(k < arr[i]){
                isok = false;
                break;
            }
        }
    }
    if(isok)    cout << ans << endl;
    else cout << -1 << endl;
    return 0;
}

F2. Same Sum Blocks (Hard) (STL+BruteForce+Greedy)

#include <iostream>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
using namespace std;
const int MAX = 1505;
int arr[MAX];
struct query{
    int l,r;
    bool operator < (const query & h){
        return r == h.r? l < h.l : r < h.r;
    }
}Query[MAX*MAX];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int n;  cin >> n;
    for(int i = 1; i <= n; ++i){
        cin >> arr[i];
    }
    set<int>st;
    map<int,vector<query> > mp;
    int tot = 0;
    for(int i = 1; i <= n; ++i){
        int sum = arr[i];
        st.insert(sum);
        mp[sum].push_back(query{i,i});
        for(int j = i+1; j <= n; ++j){
            sum+=arr[j];
            st.insert(sum);
            mp[sum].push_back(query{i,j});
        }
    }
    vector<query> vec;
    for(auto i : st){
        sort(mp[i].begin(),mp[i].end());
        vector<query>tt;
        int r = -1;
        for(auto j : mp[i]){
            query t = j;
            if(t.l > r ){
                r = t.r;
                tt.push_back(t);
            }
        }
        if(tt.size() > vec.size()) vec = tt;
    }
    cout << vec.size() <<endl;
    for(int i = 0; i < vec.size(); ++i){
        cout << vec[i].l << ' ' << vec[i].r<<endl;
    }
    return 0;
}
 

Codeforces Round #509 (Div. 2) E.Tree Reconstruction(思维,构造)

思路:
很容易发现,一个树,删掉一条边之后,其中一棵子树的权值一定是n,所以先看输入的maxa,maxb 中 maxb是不是为n,如果不是,就可以直接输出NO了。
然后对于 maxa,统计一下maxa出现的个数,然后往 maxa-maxb之间插入点就可以了。注意一下细节即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
map<int,int> mp;
vector<pair<int,int>> vec;
bool vis[maxn];
int main()
{
	int n;	cin >> n;
	bool isok = true;
	for(int i = 0,a,b; i < n-1; ++i){   // b 只能为 n
		cin >> a >> b;
		mp[a]++;
		if(b!=n)	isok = false;
	}
	if(!isok){
		cout << "NO" << endl;
		return 0;
	}
	for(auto i : mp){
		vis[i.first] = 1;
		if(i.second > i.first){
			cout << "NO" << endl;
			return 0;
		}
		int cnt = 1;
		int now = i.first;
		for(int j = 1; j < i.first && cnt < i.second; ++j){
			if(vis[j])	continue;
			vis[j] = true;	vec.push_back(make_pair(now,j));
			now = j;	cnt++;
		}
		vec.push_back(make_pair(now,n));	vis[now] = 1;
		if(cnt!=i.second){
			cout << "NO" << endl;
			return 0;
		}

	}
	cout << "YES" << endl;
	for(auto i : vec){
		cout << i.first << ' ' << i.second << endl;
	}
	return 0;
}

B. Chemical table 并查集

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int pre[maxn<<1];
int FindPre(int x){
    return pre[x] == x? x : pre[x] = FindPre(pre[x]);
}
inline void Union(int x,int y){
    int fx = FindPre(x),fy = FindPre(y);
    if(fx!=fy) pre[fy] = fx;
}
bool vis[maxn<<1];
int main()
{
    int n,m,q;  cin >> n >> m >> q;
    for(int i = 1; i <= n+m; ++i)   pre[i] = i;
    for(int i = 0; i < q; ++i){
        int r,c;    cin >> r >> c;
        c += n;
        Union(r,c);
    }
    int ans = -1;
    for(int i = 1; i <= n+m; ++i){
        int root = FindPre(i);
        if(!vis[root]){
            vis[root] = true;
            ans++;
        }
    }
    cout << ans << endl;
    return 0;
}

update (2019/11/22)
分治: Codeforces 448C

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3+5;
const int INF = 0x3f3f3f3f;
int arr[maxn];
int n;
inline int DC(int l,int r,int now){
    if(r-l==1)    return 1;
    int minn = INF;
    for(int i = l; i < r; ++i) minn = min(minn,arr[i]);
    int ans = minn - now;
    for(int i = l,j; i < r; ++i){
        if(arr[i] > minn){
            for(j = i; j < r && arr[j] > minn; ++j);
            ans += DC(i,j,minn);
            i = j;
        }
    }
    return min(r-l,ans);
}
int main()
{
    ios::sync_with_stdio(false);    cin.tie(0); cout.tie(0);
    cin >> n;
    for(int i = 0; i < n; ++i){
        cin >> arr[i];
    }
    cout << DC(0,n,0) << endl;
    return 0;
}

260A - Adding Digits(思维题)
可以先遍历0~9,加到a的右边看一下是不是能被b整除,如果可以的话,后面的n-1个数都补0即可。

1255D - Feeding Chicken
分析:要把土地分成k块,使得每块之间的权值差距最小,我们可以先把总权值算出来,然后平均分配即可,然后就遍历整个矩阵,给其染色就可以了

#include <bits/stdc++.h>
using namespace std;
const int maxn = 105;
char color[maxn];
int arr[maxn];
char G[105][105];
int main()
{
    ios::sync_with_stdio(false);    cin.tie(0); cout.tie(0);
    int T;  cin>>T;
    for(int i = 0; i < 10; ++i) color[i] = i + '0';
    for(int i = 10,j = 0; i <= 62; i+=2,++j)   color[i] = 'A'+j,color[i+1] = 'a'+j;
    while(T--){
        int r,c,k;  cin >> r >> c >> k; int cnt = 0;
        for(int i = 0; i < r; ++i){
            for(int j = 0; j < c; ++j){
                cin >>G[i][j];  if(G[i][j]=='R')    ++cnt;
            }
        }
        int num = cnt/k;
        for(int i = 0; i < k; ++i)  arr[i] = num;
        for(int i = 0; i < cnt%k; ++i)  arr[i] += 1;
        int now = 0;
        bool isok  = false;;
        for(int i = 0; i < r; ++i){
            if(isok){
                for(int j = 0; j < c; ++j){
                    if(G[i][j]=='R')    arr[now]--;
                    G[i][j] = color[now];
                    if(arr[now]==0 && now < k-1) ++now;
                }
            }
            else{
                for(int j = c-1; j >= 0; --j){
                    if(G[i][j]=='R')    arr[now]--;
                    G[i][j] = color[now];
                    if(arr[now]==0 && now < k-1) ++now;
                }
            }
            isok = !isok;
        }
        for(int i = 0; i < r; ++i){
            for(int j = 0; j < c; ++j)  cout << G[i][j];
            cout << endl;
        }
 
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值