程序设计组4题解

Posted on 2022-08-08 20:49  ytdnf  阅读(36)  评论(0编辑  收藏  举报

幸运数字Ⅱ

打表先预处理出所有情况,然后可以二分或线性找到左端点右端点,然后逐个处理
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>
#include<unordered_map>

using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int N = 2e5 + 7;
int n, m;
ll ans;
ll a[N];
int cnt;

void dfs(ll t)
{
	a[cnt++] = t;
	if (t > 1e9) return;
	dfs(t * 10 + 4);
	dfs(t * 10 + 7);
}

void solve()
{

	dfs(4);
	dfs(7);
	sort(a, a + cnt);
	ll l, r;
	cin >> l >> r;
	int t1 = lower_bound(a, a + cnt, l) - a;//左端点
	int t2 = lower_bound(a, a + cnt, r) - a;//右端点
	if (t2 == cnt) t2--;
	for (int i = t1; i <= t2; i++) {
		ans += a[i] * (min(a[i],r) - l + 1);
		l = a[i] + 1;
	}
	cout << ans;
}


int main()
{
	cin.sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	solve();

}

英语作文

可以用map+队列存储每个字符串的下标,当输入一个字符串时就判断其队首是否符合条件,
因为是有序加入的,若前面的元素满足条件,则后面的元素也满足条件,对于不满足条件的直接pop就行,
此时该位置的字符串就可以和队列中剩余元素组成队列的大小对单词
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>
#include <ctime>
#include<unordered_map>
#include <bitset>
#include<random>

using namespace std;
typedef long long ll;

map<string, queue<int>> mm;
int n, k;
int ans;

void solve()
{
    cin >> n >> k;
    for (int i = 0; i < n; i++)
    {
        string s;
        cin >> s;
        while (!mm[s].empty() && i - mm[s].front()-1 > k) mm[s].pop();//不满足弹出
        ans += mm[s].size();//累加答案
        mm[s].push(i);//将该位置加入
    }
    cout << ans << endl;
}


signed main()
{
    cin.sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
}

好串

栈的运用,该题等价于判断括号是否匹配,只要把a看成左括号,b看成右括号即可
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>
#include <ctime>
#include<unordered_map>

using namespace std;
typedef long long ll;
#define int long long 
#define endl "\n"
#define pb push_back
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
#define pr pair<int,int>
const int N = 1e6 + 7;
const int mod = 1e9 + 7;


void solve()
{
    stack<int> s;
    string p;
    cin>>p;
    int len=p.size();
    for(int i=0;i<len;i++)
    {
        if(p[i]=='a')
        {
            s.push('a');
        }else {
           if(!s.empty()&&s.top()=='a') s.pop();
            else {
                cout<<"Bad"<<endl;
                return ;
            }
        }
    }
    if(s.size()) cout<<"Bad"<<endl;
    else cout<<"Good"<<endl;
}

signed main()
{
    cin.sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
}

漂亮数

素数筛+前缀和
在筛选过程中把符合条件的顺便筛选出来,然后再求一个前缀和,即可在O(1)时间内回答各次询问
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>


using namespace std;
typedef long long ll;

#define endl "\n"
const int N = 1e8 + 7;
const int mod = 1e9 + 7;

int prime[N];
int cnt;
bool vis[N];
int vis1[N];

inline void solve()
{
    for (int i = 2; i < N; i++)//素数筛
    {
        if (!vis[i]) prime[cnt++] = i;
        for (int j = 0; j < cnt; j++)
        {
            if (i * prime[j] > N) break;
            if (!vis[i]) vis1[prime[j] * i] = 1;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    for (int i = 1; i < N; i++)
    {
        vis1[i] = vis1[i - 1] + vis1[i];
    }
    int q;
    scanf("%d", &q);
    while (q--)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", vis1[r] - vis1[l - 1]);
    }
}

signed main()
{
    solve();
}

第k小

优先队列
优先队列加入某元素时会按照堆排序,可保证每次访问的堆顶元素是最大值或最小值(具有最高优先级或最低优先级),大顶堆会优先访问最大值(最高优先级),小顶堆反之
那么该题我们可以用优先队列去保存k个值,
考虑新加入一个元素,
若当前队列大小小于k,直接加入即可
若比当前k个元素最大值大或相等,那么丢弃即可,因为其排位一定是在k个之后
若比最大值小,那么将堆顶的元素弹出,再将其加入即可,此时根据优先队列性质,堆顶还会是最大的元素
那么询问时直接根据是否符合要求输出堆顶元素即可
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include<unordered_map>

using namespace std;
typedef long long ll;
#define int long long 
#define  x first
#define  y second
const int N=2e5+7;

int n, m, k;
int a[N];
priority_queue<int, vector<int>, less<int>> q;


void solve()
{
	cin >> n >> m >> k;
	for (int i = 0; i < n; i++) cin >> a[i];
	sort(a, a + n);
	for (int i = 0; i < min(n,k); i++) q.push(a[i]);
	while (m--)
	{
		int v;
		cin >> v;
		if (v == 2) {
			if (k > q.size()) cout << "-1\n";
			else cout << q.top() << "\n";
		}
		else {
			int tt;
			cin >> tt;
			if (k > q.size()) q.push(tt);
			else if (tt < q.top()) q.pop(),q.push(tt);
		}
	}

}

signed main()
{
	cin.sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	solve();
}

毒瘤xor

思维+前缀和+位运算
假设当前区间的长度为len,那么可以考虑二进制某一个位置上0或1的数量,这俩加起来肯定是等于n的
区间某一位1的数量可以用前缀和来维护
若某一位1的数量为t,如果t<=len/2,那么说明该位置上,0的数量大于等于1的数量,此时如果令该位为1则该位置上的1最多,如果t>len/2,则令该位置为0,
如果第i位为1,那么其对答案的贡献为1<<i(即让答案的第i位为1)
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>

using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
const int Maxx = 2e5 + 7;
const ll mod = 998244353;

int sum[N][34];
int n,q;

void solve()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        int v;
        scanf("%d", &v);
        for (int j = 0; j < 31; j++)//维护每位的前缀和
        {
            if ((1 << j) & v) sum[i][j] = sum[i - 1][j] + 1;
            else sum[i][j] = sum[i - 1][j];
        }
    }
    scanf("%d", &q);
    while (q--)
    {
        ll ans = 0;
        int l=0, r=0;
   
        scanf("%d%d", &l, &r);
        int len = r - l + 1;
        for (int i = 0; i < 31; i++)
        {
            int tt = sum[r][i] - sum[l - 1][i];//区间内第i位1的数量
            if (tt <= len / 2) ans += (1ll << i);
        }
        printf("%lld\n", ans);
    }
}

int main()
{
    solve();

}

八数码

bfs(dfs应该也行)
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>
#include<unordered_map>

using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int N = 1e5 + 7;
int n, m;
char g[3][3];
int sx, sy;
int flag;
char op[N];
map<string, int> mm;
int mov[4][2] = { -1,0,1,0,0,-1,0,1 };
char o[4] = { 'u','d','l','r' };
int cnt;

struct node {
	int x, y;
	char g[3][3];
	string s;
};

bool check(char g[][3])//判断是否符合要求
{
	int cnt = 1;
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			if (i == 2 && j == 2 && g[i][j] == 'x') return true;
			else if (g[i][j] != cnt + '0') return false;
			cnt++;
		}
	}
	return true;
}

string getkey(char g[][3])//判重
{
	string t;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) t += g[i][j];
	return t;
}

void bfs()
{
	queue<node> q;
	node qr;
	memcpy(qr.g, g, sizeof(qr.g));
	qr.s = "";
	qr.x = sx;
	qr.y = sy;
	q.push(qr);
	while (!q.empty())
	{
		qr = q.front();
		q.pop();
		memcpy(g, qr.g, sizeof g);
		if(mm.count(getkey(g))) continue;
		mm[getkey(g)] = 1;
		if (check(g)) {
			cout << qr.s;
			return;
		}
		string s = qr.s;
		for (int i = 0; i < 4; i++)
		{
			int x = qr.x + mov[i][0];
			int y = qr.y + mov[i][1];
			if (x >= 0 && x <= 2 && y >= 0 && y <= 2)
			{
				swap(g[x][y], g[qr.x][qr.y]);
				
				node te;
				te.x = x;
				te.y = y;
				te.s = s + o[i];
				memcpy(te.g, g, sizeof(te.g));
				q.push(te);
				swap(g[x][y], g[qr.x][qr.y]);
			}
		}
	}
	cout << "unsolvable";
}

void solve()
{
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) {
			cin >> g[i][j];
			if (g[i][j] == 'x') sx = i, sy = j;
		}
	bfs();

}


int main()
{
	cin.sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	solve();

}

拦截导弹

最长单调递增/递减子序列的经典问题
对于题目中第一个问题,即求最长单调递减子序列
第二个问题即求最长单调递增子序列
即每询问一个位置,将其放入到一个临时数组里,该放入位置即当前位置的最优解
放入的时候可以二分查询放入位置
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>
#include <ctime>
#include<unordered_map>

using namespace std;
typedef long long ll;
#define int long long 
#define endl "\n"
#define pb push_back
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
#define pr pair<int,int>
const int N = 1e6 + 7;
const int mod = 1e9 + 7;

int ans;
int res;
void solve()
{
  vector<int> v;
  int x;
    while(cin>>x){
        v.push_back(x);
    }
    vector<int> c(v.size(),3000000);
    int cnt=0;
    c[0]=v[0];
    for(int i=1;i<v.size();i++)//求最长单调递增子序列
    {
        if(v[i]>c[cnt]) cnt++,c[cnt]=v[i],ans=max(ans,cnt+1);//大于最后一个数就加入
        else {
            int k=lower_bound(c.begin(), c.end(),v[i])-c.begin();//二分查询插入位置
            ans=max(k+1,ans);//更新答案
            c[k]=v[i];
        }
    }
    for(int i=0;i<v.size();i++) c[i]=3000000;
    cnt=0;
    reverse(v.begin(),v.end());//将容器元素反过来
    c[0]=v[0];
    for(int i=1;i<v.size();i++)//求最长单调递减子序列,即求单调递增子序列的逆过程
    {
        if(v[i]>c[cnt]) cnt++,c[cnt]=v[i],res=max(res,cnt+1);
        else {
            int k=lower_bound(c.begin(), c.end(),v[i])-c.begin();
            res=max(k+1,res);
            c[k]=v[i];
        }
    }
    cout<<res<<endl;
    cout<<ans;
}

signed main()
{
    cin.sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    //cout << fixed << setprecision(1) << sin(2) + sin(5) + sin(6);
    solve();
}

合并回文子串

区间dp
状态存储:dp[i][j][k][l]
s字符串中i-j与p字符串中k-l位置的最大价值
那么状态转移方程为:
dp[i][j][k][l]|=dp[i+1][j-1][k][l] (s[i] == s[j])
dp[i][j][k][l]|=dp[i][j][k+1][l-1] (p[k] == p[l])
dp[i][j][k][l]|=dp[i+1][j][k][l-1] (s[i] == p[l])
dp[i][j][k][l]|=dp[i][j-1][k+1][l] (s[j] == p[k])

代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>


using namespace std;
typedef long long ll;
#define int long long 
#define endl "\n"
const ll mod = 998244353;
const int N = 1e6 + 7;

string s;
string p;
bool dp[57][57][57][57];
int ans;
int t;

inline void solve()
{
	ans = 0;
	memset(dp, 0, sizeof dp);
	cin >> s;
	cin >> p;
        s=" "+s;
        p=" "+p;
	int lens = s.size();
	int lenp = p.size();
	for (int i = 0; i < lens; i++)
	{
		for (int j = 0; j < lenp; j++)
		{
			for (int k = 1, l = i; l < lens; k++, l++)
			{
				for (int m = 1, d = j; d < lenp; m++, d++)
				{
					if (i+j<=1) dp[k][l][m][d] = 1;//长度为1的情况
					else {
						if (s[k] == s[l] &&i) dp[k][l][m][d] |= dp[k + 1][l - 1][m][d];
						if (p[m] == p[d] &&j) dp[k][l][m][d] |= dp[k][l][m + 1][d - 1];
						if (s[k] == p[d] && i && j) dp[k][l][m][d] |= dp[k + 1][l][m][d - 1];
						if (s[l] == p[m] && i && j) dp[k][l][m][d] |= dp[k][l - 1][m + 1][d];
					}
					if (dp[k][l][m][d]) ans = max(ans, i + j);
				}
			}
		}
	}
	cout << ans << "\n";
}

signed main()
{
	cin.sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> t;
	while (t--) solve();
}

区区区间

线段树
还不会线段树的话,推荐一篇博客:https://www.cnblogs.com/poi-bolg-poi/p/11055093.html
区间修改注意左端点和右端点
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>
#include <ctime>
#include<unordered_map>

using namespace std;
typedef long long ll;
#define int long long 
#define endl "\n"
#define pb push_back
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
#define pr pair<int,int>
const int N = 2e5 + 7;
const int mod = 1e9 + 7;

int ar[N];
int tree[N*4];
int laze[N*4];
int L[N * 4];
int R[N * 4];
int lzl[N * 4];
int lzr[N * 4];


void push_up(int node)//向上更新
{
    tree[node] = tree[node * 2] + tree[node * 2 + 1];
}

void push_down(int node)//向下打标记
{
    if (laze[node])
    {
        laze[node * 2] = laze[node];
        laze[node * 2+1] = laze[node];
        lzl[node * 2] = lzl[node];
        lzr[node * 2] = lzr[node];
        lzl[node * 2 + 1] = lzl[node];
        lzr[node * 2 + 1] = lzr[node];
        int l = lzl[node];
        int r = lzr[node];
        int ll = L[node * 2] - l+laze[node];
        int rr = R[node * 2]-L[node*2] + ll;
        tree[node * 2] = (rr - ll + 1) * (ll + rr) / 2;
        ll = L[node * 2 + 1] - l + laze[node];
        rr = R[node * 2 + 1]-L[node*2+1] + ll;
        tree[node * 2 + 1] = (rr - ll + 1) * (ll + rr) / 2;
        laze[node] = 0;
        lzl[node] = 0;
        lzr[node] = 0;
    }
}

void build(int node,int l, int r)//建树
{
    L[node] = l;
    R[node] = r;
    if (l == r)
    {
        tree[node] = ar[l];
        return;
    }
    int mid = l + r >> 1;
    if (mid >= l) build(node * 2, l, mid);
    if (mid < r) build(node * 2 + 1, mid + 1, r);
    push_up(node);
}

void update_range(int node, int l, int r, int L,int R,int v)//区间修改
{
    if (L >= l && R <= r)
    {
        int ll = L-l + v;
        int rr =  R-L + ll;
        tree[node] = (rr - ll + 1) * (ll+rr)/2;
        laze[node] += v;
        lzl[node] = l;
        lzr[node] = r;
        return;
    }
    push_down(node);
    int mid = L + R >> 1;
    if (mid >= l) update_range(node * 2, l, r, L, mid, v);
    if (mid < r) update_range(node * 2 + 1, l, r, mid + 1, R, v);
    push_up(node);
}

int query_range(int node, int l, int r, int L, int R)//区间查询
{
    if (L >= l && R <= r) {
        return tree[node];
    }
    int sum = 0;
    push_down(node);
    int mid = L + R >> 1;
    if (mid >= l) sum += query_range(node * 2, l, r, L, mid);
    if (mid < r) sum += query_range(node * 2 + 1, l, r, mid + 1, R);
    return sum;
}

void solve()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> ar[i];
    build(1, 1, n);
    for (int i = 0; i < m; i++)
    {
        int op;
        cin >> op;
        if (op == 1)
        {
            int l, r, k;
            cin >> l >> r >> k;
            update_range(1, l, r, 1, n, k);
        }
        else
        {
            int l, r;
            cin >> l >> r;
            cout << query_range(1, l, r, 1, n)<<endl;
        }
    }
}

signed main()
{
    cin.sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
}

中序序列

树的遍历,递归
因为前序遍历是先根再左再右,后序遍历是先左右再根,
那么前序遍历第一个为根节点,后序遍历最后一个为根节点
根据题目描述,根结点右边的第一个结点总是左儿子结点(若只给出一棵树的前序和后序遍历,是无法还原该树的的)。
在该二叉树的后序遍历中,知道了该根结点和其左儿子在后序遍历中的下标后,夹在根结点和其左儿子之间的结点是根结点的右子树。在左儿子的左边部分结点是该左儿子的子树。
所以就可以寻找该左儿子的下标后进行递归求中序序列
代码:

class Solution {
public:
    vector<int> v;
    //ps前序序列起点,pe前序序列终点,ss后序序列起点,se后序序列终点
    void deal(vector<int> &pre,int ps,int pe,vector<int> &suf ,int ss,int se)
    {
          if(ps>pe||ss>se) return ;
          if(ps==pe)
          {
              v.push_back(pre[ps]);
              return;
          }
        int i=0;
        for(;i<suf.size();i++)
        {
            if(suf[ss+i]==pre[ps+1]) break;
        }
         deal(pre,ps+1,ps+i+1,suf,ss,ss+i);
         v.push_back(pre[ps]);
         deal(pre,ps+i+2,pe,suf,ss+i+1,se-1);
       
    }
    
    vector<int> solve(int n, vector<int>& pre, vector<int>& suf) {
        // write code here
        deal(pre,0,n-1,suf,0,n-1);
        return v;
    }
};

数一数

kmp的应用
该题只需考虑长度最小的字符串,因为长度长的字符串在长度短的字符串中肯定是没有子串的,那么该长串的答案为0,且若最短字串中在任意字符串中未出现,那么所有的答案都为0
代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>
#include <ctime>
#include<unordered_map>
#include <bitset>
#include<random>

using namespace std;
typedef long long ll;
#define int long long 
#define endl "\n"
#define pb push_back
#define pr pair<int,int>
#define all(a) a.begin(),a.end()
#define fr first
#define se second
const int N = 2e6 + 7;
const int maxx = 507;
const int mod = 998244353;

int n;
string s[N];
int ne[N];

void getne(string p)//kmp核心求next数组
{
    int i = 0;
    int k = -1;
    ne[0] = -1;
    while (i < p.size())
    {
        if (k == -1 || p[i] == p[k]) ne[++i] = ++k;
        else k = ne[k];
    }
}

int kmp(string s, string p)//求字串出现次数
{
    int i = 0;
    int j = 0;
    int res = 0;
    while (i < s.size())
    {
        while (i < s.size() && s[i] != p[j]) i++;
        while (s[i] == p[j] && i < s.size() && j < p.size()) i++, j++;
        if (j == p.size()) {
            res++;
        }
        while (j != -1 && s[i] != p[j]) j = ne[j];
        if (j == -1) j++;
    }
    return res;
}


void solve()
{
    cin >> n;
    string tt;
    int minlen = 1e9 + 7;
    for (int i = 0; i < n; i++)
    {
        cin >> s[i];
        if (!i) tt = s[i], minlen = s[i].size();
        else if (s[i].size() < minlen) tt = s[i], minlen = s[i].size();
    }
    getne(tt);
    int ans = 1;
    for (int i = 0; i < n; i++)
    {
        ans = ans * kmp(s[i], tt) % mod;
        if (ans == 0) break;
    }
    for (int i = 0; i < n; i++)
    {
        if (s[i] == tt) cout << ans << endl;
        else cout << 0 << endl;
    }
}


signed main()
{
    cin.sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
}




Copyright © 2025 ytdnf
Powered by .NET 9.0 on Kubernetes