程序设计组一题解

Posted on 2022-06-30 10:43  ytdnf  阅读(146)  评论(0)    收藏  举报

校门外的树

主要是差分的思想,当然数据量较小暴力也能做,用一个数组a[],假设l-r区间的树被移除,则a[l]--,a[(r+1)++],然后对a求前缀和,若其为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 <set>


using namespace std;

int L[100007];
int m;

int main()
{
    int len;
    cin >> len >> m;
    for (int i = 0; i < m; i++)
    {
        int l, r;
        cin >> l >> r;
        L[l]++;
        L[r+1]--;
    }
    int t = 0;
    int ans = 0;
    for (int i = 0; i <= len; i++)
    {
         t+=L[i];
        if(!t) ans++;
        
    }
    cout << ans << endl;
}

序列求和

模运算:
(a + b) % p = (a % p + b % p) % p
(a - b) % p = (a % p - b % p) % p
(a * b) % p = (a % p * b % p) % p
对于除法的模运算,需要用逆元将除法变为乘法
逆元:整数a,b,满足a * b = 1(mod m),那么称b是a的模m乘法逆元
比如:A/B%C我们可以写成A * (1 / B)% C,这样就是AX%C的形式
x即为a模m的乘法逆元
https://www.cnblogs.com/daybreaking/p/9342060.html
求逆元可参考 以上链接
代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll mod = 1e9 + 7;

ll ksm(ll a, ll b)
{
    ll s = 1;
    while (b)
    {
        if (b & 1) s = s * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return s;
}

int main()
{
    ll inv_6 = ksm(6, mod - 2);
    ll a;
    while (cin >> a)
    {
        a %= mod;
        printf("%lld\n", a * (a + 1) % mod * (a * 2 + 1) % mod * inv_6 % mod);
    }
}

子串

暴力枚举就行,注意当进制大于10的时候10为'A',11为'B'.....
代码:

#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;
int ans;
int n;
string s;

string gets(int wi, int n)
{
    string s="";
    for (int i = 1; i <= n; i++)
    {
        string t = "";
        int tt = i;
        stack<int> ss;
        while (tt)
        {
            ss.push(tt % wi);
            tt /= wi;
        }
        if (tt) ss.push(tt);
        while (!ss.empty())
        {
            int t1 = ss.top();
            ss.pop();
            if (t1 >= 10) t += (char)(t1 - 10 + 'A');
            else t += to_string(t1);
        }
        s += t;
    }
    return s;
}


void solve()
{
    cin >> n;
    cin >> s;
    for (int i = 2; i <= 16; i++)
    {
        if (gets(i, n).find(s) != string::npos) {
            cout << "yes\n";
            return;
        }
    }
    cout << "no";
}


int main()
{
    //cin.tie(0);
    //cout.tie(0);
   solve();
    //cout << gets(16, 15);
}


数学考试

前缀和+尺取法
因为区间为k,所以可以枚举每一个区间,用一个数组maxx[]来存储i以及i以后每一个符号条件区间的和的最大值,然后从前往后枚举即可
因为有负数,记得初始化maxx数组
代码:

#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 = 2e5 + 7;

int n;
ll ans;
ll t;
ll a[N];//前缀和
ll maxx[N];//保存从i往后选取连续k个数的最大值
ll k;

void solve()
{
	ans = -1e18;
	cin >> n >> k;
	for (int i = 1; i <= n; i++)
	{
		maxx[i] = -1e18;
		cin >> a[i];
		a[i] += a[i - 1];
	}
	for (int i = n - k+1; i >= 1; i--)
	{
		maxx[i] = max(maxx[i + 1], a[i + k-1] - a[i-1]);
	}
	for (int i = 1; i+2*k-1 <=n ; i++)
	{
		ans = max(a[i + k-1] - a[i - 1]+maxx[i + k],ans);
	}
	cout << ans << endl;
}

int main() {
	cin.tie(0);
	cout.tie(0);
	cin >> t;
	while (t--)
	{
		solve();
	}
	//solve();
	return 0;
}

字符串

尺取法
依次从前往后,枚举左右端点,[l,i]表示区间,用i表示右端点,l表示符合条件的左端点,
若[l,i]不符合,将右端点右移,
若符合,将左端点不断右移直至不符合,在这个过程中更新答案即可
检查区间是否符合可用一个数组arr[]记录区间中a-z的个数,若其中一个为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 <set>

using namespace std;
typedef long long ll;

const int N = 2e4 + 7;
string s;
int a[26];
int ans;

bool check()//检查是否符合条件
{
	for (int i = 0; i < 26; i++)
		if (!a[i]) return false;
	return true;
}

void solve()
{
	cin >> s;
	ans = s.size();
	int l = 0;
	for (int i = 0; i < s.size(); i++)
	{
		a[s[i] - 'a']++;//更新检查数组
		while (check()&&l<i)
		{
			ans = min(ans, i - l + 1);//更新答案
			a[s[l] - 'a']--;//更新检查数组
			l++;//符合左端点右移
		}
	}
	cout << ans << endl;
}

int main() {
	solve();
	return 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 <set>

typedef long long ll;
using namespace std;


#define x first
#define y second
const int N = 2e5 + 7;
const ll mod = 1e9 + 7;

string s;
int n;

bool check()
{
    int i = 0;
    int j = s.size() - 1;
    while (i<j)
    {
        if (s[i] != s[j]) return false;
        i++;
        j--;
    }
    return true;
}

int getnum(char c)
{
    if (isdigit(c)) return c - '0';
    else   return c - 'A' + 10;
}

char getc(int num)
{
    if (num < 10) return num + '0';
    else return num - 10 + 'A';
}

void add()
{
    int flag = 0;
    reverse(s.begin(), s.end());
    string tt = s;
    reverse(s.begin(), s.end());
    int len = s.size();
    for (int i = len - 1; i >= 0; i--)
    {
        int t1 = getnum(s[i]);
        int t2 = getnum(tt[i]);
        t1 += t2;
        if (t1 < n) s[i] = getc(t1);
        else {
           
                s[i] = getc(t1 % n);
                int t1 = i - 1;
                while (t1>=0&&getnum(s[t1])==n-1)
                {
                    s[t1] = '0';
                    t1--;
                }
                if (t1 == -1) {
                    flag = 1;
                    continue;
                }
                s[t1] = getc(getnum(s[t1]) + 1);
           
        }
    }
    if (flag) s = '1' + s;
}

void solve()
{   
    cin >> n;
    cin >> s;
    for (int i = 1; i <= 30; i++)
    {
        add();
        //cout << s << endl;
        if (check()) {
            cout << "STEP=" << i;
            return;
        }
    }
    cout << "Impossible!";
}


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


玩具谜题

首先用一个数组按顺序保存其信息即可
也是一个模拟吧,若当前的人朝内,即其状态是0,则向左即减,向右即加
若当前的人朝外,即其状态是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;
int ans;
int n, q;
struct node {
    int z;//状态
    string name;
}L[N];

void solve()
{
    cin >> n >> q;
    for (int i = 0; i < n; i++) cin >> L[i].z >> L[i].name;
    int last = 0;
    for (int i = 0; i < q; i++)
    {
        int z, step;
        cin >> z >> step;
        if (z == 0) {
            if (L[last].z == 0) {
                last = (last - step+n) % n;
            }
            else {
                last = (last + step) % n;
            }
        }
        else if (z == 1)
        {
            if (L[last].z == 0) {
                last = (last + step) % n;
            }
            else {
                last = (last - step+n) % n;
            }
        }

    }
    cout << L[last].name;
}

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

}


字符串的展开

模拟,题目可能有点长,但其实应该挺好写的
代码:


#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>

typedef long long ll;
using namespace std;

#define x first
#define y second
const int N = 2e5 + 7;
const ll mod = 1e9 + 7;

int p1, p2, p3;
string s;

void print(char s, char e)
{
    int l;//开始位置
    int tt;//输出字母还是数字,若是字母,是否大小写
    int r;//结束位置
    if (isdigit(s)) tt = '0', l = s + 1 - '0', r =e-1-'0';
    else {
        l = s + 1 - 'a';
        r = e - 1 - 'a';
        if (p1 == 1) tt = 'a';
        else if (p1 == 2) tt = 'A';
    }
    if (p1 == 3)//输出*
    {
        for (int i = l; i <= r; i++)
        {
            for (int j = 0; j < p2; j++)
            {
                cout << "*";
            }
        }
        return;
    }
    //逆序输出
    if (p3 == 1) {
        for (int i = l; i <= r; i++)
        {
            char c = i + tt;
            for (int j = 0; j < p2; j++)
            {
                cout << c;
            }
        }
    }
    //正序输出
    else {
        for (int i = r; i >= l; i--)
        {
            char c = i + tt;
            for (int j = 0; j < p2; j++)
            {
                cout << c;
            }
        }
    }
}

void solve()
{
    cin >> p1 >> p2 >> p3;
    cin >> s;
    for (int i = 0; i < s.size(); i++)
    {
        if (s[i] == '-'&&i&&i!=s.size()-1) {
            if (isdigit(s[i - 1]) && isdigit(s[i + 1])) {
                if (s[i - 1] < s[i + 1]) {
                    print(s[i - 1], s[i + 1]);
                    continue;
                }
            }
            if (islower(s[i - 1]) && islower(s[i + 1])) {
                if (s[i - 1] < s[i + 1]) {
                    print(s[i - 1], s[i + 1]);
                    continue;
                }
            }
             cout << s[i];
        }
        else cout << s[i];
    }
}


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


减法和除法

思维题,可以看n/2和n-x哪个更小,选择结果小的操作即可
代码:

#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 = 2e5 + 7;

int n;
int x;
int ans;

void solve()
{
	cin >> n >> x;
	while (n>0)
	{
		if (n / 2 < n - x) n /= 2;
		else n -= x;
		ans++;
	}
	cout << ans;
}

int main() {
	cin.tie(0);
	cout.tie(0);
	solve();
	//solve();
	return 0;
}

减法和求余

也是一个思维题,假设给了一个数组,那么最多2次操作即可,第一次对全对2取余,第二次将数组中的1全部减一即可
若数组全为0,则不用操作,即ans=0
当还有以下情况,只用操作一次:
1.数组中除了0全为1
2.当数组中的所有数的最大公约数相同
其他情况即为2

代码:

#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 = 2e5 + 7;

int n;
int v;
int t0;//0的个数
int t1;//1的个数
int ans = 2;


int gcd(int a, int b)//求最大公约数
{
	return b ? gcd(b, a % b) : a;
}

void solve()
{
	scanf("%d", &n);
	int last;
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &v);
		if (!i) last = v;
		else last = gcd(v, last);
		if (v == 0) t0++;
		else if (v == 1) t1++;
    }
    if(t0==n) ans=0;
    else if(t0+t1==n) ans=1;
    else if (last != 1)  ans = 1;
    else  ans = 2;
    printf("%d", ans);
}

int main() {
	//cin.tie(0);
	//cout.tie(0);
	solve();
	return 0;
}

被3整除的子序列

假设我们有一个字符串456,其字串构成可以从前往后遍历,每次在之前的子串前加上该位置的字符,再将此字符加入

当前位置 子串
1 4
2 4,45,5
3 4,45,5,46,456,56,6

那么对于该题,我们可以考虑每一位除以3后的余数
若一个数取余3为1,另一个数取余3为0,那么这俩个数组合起来取余3是为1的,其他的依次类推
对数组dp[][3],一维i为当前位置
dp[i][0]保存其除以3后余0,即被3整除的数的字串个数,dp[i][1]保存其被3后余1的字串个数,dp[i][2]保存其被3后余1的字串个数
那么可得到状态转移方程为
若当前字符取余3后为0,则有:
dp[i][0] = 2 * dp[i - 1][0] + 1
dp[i][1] = dp[i - 1][1] * 2
dp[i][2] = dp[i - 1][2] * 2

若当前字符取余3后为1,则有:
dp[i][0] = dp[i - 1][0] + dp[i - 1][2]
dp[i][1] = dp[i - 1][0] + dp[i - 1][1] + 1
dp[i][2] = dp[i - 1][1] + dp[i - 1][2]

若当前字符取余3后为2,则有:
dp[i][0] = dp[i - 1][0] + dp[i - 1][1]
dp[i][1] = dp[i - 1][2] + dp[i - 1][1]
dp[i][2] = dp[i - 1][2] + dp[i - 1][0] + 1

最后答案即为dp[最后一次处理位置][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 <set>

using namespace std;
typedef long long ll;
const int N = 5e4 + 7;
const int Maxx = 2e5 + 7;
const ll mod = 1e9+7;
#define  x first
#define  y second
string s;

ll dp[57][3];

void solve()
{
	cin >> s;
	dp[0][(s[0] - '0') % 3] = 1;
	for (int i = 1; i <= s.size(); i++)
	{
		int tt = (s[i] - '0') % 3;
		if (tt == 0) {
			dp[i][0] = (2 * dp[i - 1][0] + 1) % mod;
			dp[i][1] = (dp[i - 1][1] * 2 ) % mod;
			dp[i][2] = (dp[i - 1][2] * 2 ) % mod;
		}
		else if (tt == 1) {
			dp[i][0] = (dp[i - 1][0] + dp[i - 1][2]) % mod;
			dp[i][1] = (dp[i - 1][0] + dp[i - 1][1] + 1) % mod;
			dp[i][2] = (dp[i - 1][1] + dp[i - 1][2]) % mod;
		}
		else if(tt==2) {
			dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
			dp[i][1] = (dp[i - 1][2] + dp[i - 1][1]) % mod;
			dp[i][2] = (dp[i - 1][2] + dp[i - 1][0] + 1) % mod;
		}
	}
	cout << dp[s.size() - 1][0];
}

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



最短路

其实就是一个spfa的模板题,spfa可以处理负边权
图的存储方式推荐用链式向前星,vector<>来模拟,因为若点数大于等于1e5的情况下,用邻接矩阵,一般会爆空间
代码:
vector处理:

#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 = 2e4 + 7;

ll dis[N];
vector<pair<int, int>> G[N];
int n, m;
int vis[N];

void spfa()
{
	queue<int> q;
	q.push(1);
	vis[1] = 1;
	while (!q.empty())
	{
		int tt = q.front();
		q.pop();
		vis[tt] = 0;
		for (int i = 0; i < G[tt].size(); i++)
		{
			int u = G[tt][i].first;
			int x = G[tt][i].second;
			if (dis[u] > x + dis[tt]) {
				if (!vis[u]) q.push(u), vis[u] = 1;
				dis[u] = dis[tt] + x;
			}
		}
	}
}

void solve()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) dis[i] = 1e18 + 7;
	dis[1] = 0;
	for (int i = 0; i < m; i++)
	{
		int v1, v2,w;
		scanf("%d%d%d", &v1, &v2, &w);
		G[v1].push_back({ v2,w });
	}
	spfa();
	for (int i = 2; i <= n; i++)
		printf("%lld\n", dis[i]);
}

int main() {
	solve();
	return 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 <set> 
#include<bitset>

typedef long long ll;
using namespace std;

const int N = 6 * 1e5 + 7;
const int INF = 0x3f3f3f3f;
struct node {
	int u, ne;
	ll w;
}edge[N];
int fa[20007];
int n, m;
ll dis[20007];
int cnt;
int u, v, w;
bool vis[20007];

inline void add(int u, int v, int w)
{
	edge[++cnt].u = v;
	edge[cnt].w = w;
	edge[cnt].ne = fa[u];
	fa[u] = cnt;
}

inline void spfa(int v)
{
	queue<int>q;
	q.push(v);
	while (!q.empty())
	{
		int fr = q.front();
		q.pop();
		vis[fr] = 0;
		for (int i = fa[fr]; i; i = edge[i].ne)
		{
			if (dis[edge[i].u] > dis[fr] + edge[i].w)
			{
				dis[edge[i].u] = dis[fr] + edge[i].w;
				if (!vis[edge[i].u]) {
					vis[edge[i].u] = 1;
					q.push(edge[i].u);
				}
			}
		}
	}
}

inline void deal()
{
	scanf("%d%d", &n, &m);
	for (int i = 2; i <= n; i++) dis[i] = INF;
	for (int i = 0; i < m; i++)
	{
		scanf("%d%d%lld", &u, &v, &w);
		add(u, v, w);
	}
	spfa(1);
	for (int i = 2; i <= n; i++) printf("%lld\n", dis[i]);
}



int main()
{
	deal();
}

/*

*/

博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3