天梯赛选拔赛题解

L1-1 签到

void solve()
{
cout<<"huan ying lai dao nan nang shi fan xue yvan ACM shi yan shi.";
}

L1-2 签到 

void solve(){
	string s;
	int ans=0;
	cin>>s;
	for(int i=0;i<s.size();i++){
		if(s[i]=='.') {continue;}
		ans=ans*10+s[i]-'0';
	}
	cout<<ans;
}

L1-3 签到

void solve(){
	int res,n;
	cin>>res>>n;
	string z="你所热爱的就是你的生活.\n";
	string y="Spongebob, here I come!\n";
	while(n--){
		int k;
		cin>>k;
		if(k<=res) cout<<y;//可以买
		else cout<<z;
	}
}

L1-4  签到 注意输出答案开longlong

int n,z,y;
long long ans;
void solve(){
	y=9999;
	cin>>n;
	while(n--){
		int s;cin>>s;
		s=min(s,y);
		ans+=s;
	}
	cout<<ans;
}

L1-5 按题意模拟

void solve(){
	int j,p,x1,x2;
	cin>>j>>p>>x1>>x2;
	//俩人都可以进,并且其中一个年龄小于禁入年龄线
	if((x1<j&&x2>=p)||(x1>=p&&x2<j)){
		cout<<x1<<"-Y "<<x2<<"-Y\n";
		if(x1>x2) 
		cout<<"qing 1 zhao gu hao 2\n";
		else 
		cout<<"qing 2 zhao gu hao 1\n";
		return;
	}
	//俩人都可以进,并且年龄都>=禁入年龄线
	if(x1>=j&&x2>=j){
		cout<<x1<<"-Y "<<x2<<"-Y\n";
		cout<<"huan ying ru guan\n";
		return;
	}
	//只有一个人可以进入
	if(x1>=j){
		cout<<x1<<"-Y "<<x2<<"-N\n";
		cout<<"1: huan ying ru guan\n";
		return;
	}
	if(x2>=j){
	cout<<x1<<"-N "<<x2<<"-Y\n";
		cout<<"2: huan ying ru guan\n";
		return;
	}
	//都无法进入
	cout<<x1<<"-N "<<x2<<"-N\n";
	cout<<"zhang da zai lai ba\n";
}

L1-6 签到

#define ld long double
int n;
ld res,ts;
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++){
		ld s;cin>>s;
		ts=1.00/(s);
		res+=ts;
	}
	res=1.00/(res*1.0/n);
	//保留两位小数
	cout<<fixed<<setprecision(2)<<res;
}

L1-7签到

int n,tz,ty;
void solve(){
	cin>>n;
	string z="You are lucky!\n";
	string y="Wish you good luck.\n";
	while(n--){
		string s;
		cin>>s;
		tz=0,ty=0;
		for(int i=0;i<s.size();i++){
			if(i<3) tz+=s[i]-'0';
			else ty+=s[i]-'0';
		}
		if(tz==ty) cout<<z;
		else cout<<y;
	}
}

L1-8 模拟

int a[8][8];
void solve(){
	for(int i=1;i<=6;i++){
		int k;cin>>k;
		a[i][k]=1;//第i个骰子,第k数字已出现
	}
	cin>>n;
/*
每个骰子摇出的点数都跟它之前任何一次出现的点数不同,
并且要求每次得到的点数最大,
所以基本策略就是每次都要投出最大的点数,
即从6开始,但出现过的点数不能再次出现*/
	for(int i=1;i<=6;i++){//第i个骰子
		int k=n;
	for(int j=6;j>=1;j--){//从6开始投K次
		if(a[i][j]) {continue;}
		k--;
		if(k==0) {cout<<j;break;}
	}
	if(i!=6) cout<<" ";//行首位不得有多余空格。
	}
}

L1-9 (灵活运用string模板库里的函数)

相关知识

 s.substr(a, b);   
 //在s中下标为a开始的b个字符,
 //包括a,如果b大于s中a下标开始后面的元素个数,则只需到末尾。
 
 
s.erase(a);        //删除下标a以及a之后的字符
s.erase(a, b);    //删除从下标a开始包括下标a在内的b个字符
 
 
string s1("Hello World");

s1.find("lo");  //在s1中从前向后查找"lo"第一次出现的地方,
//如果找到,返回"lo"开始的位置,即 'l' 所在的位置下标;
//如果找不到,返回string::npos(string中定义的静态常量 int类型时为-1)
s1.find("ll", 1); //从下标为1的地方开始往后找
s1.rfind("lo");  //从后向前查找,返回 'l' 所在位置的下标
s1.find_first_of("abcd"); //在s1中从前向后找,"abcd"中任何一个字符第一次出现的地方。
s1.find_last_of("abcd"); //最后一次出现的地方,即从后往前找第一次出现的地方。
s1.find_first_not_of("abcd");  //不在"abcd"中的字母第一次出现的地方。
s1.find_last_not_of("abcd");  //不在"abcd"中的字母最后一次出现的地方。

 
 
 
 
 
 
 
 
 
 
#include<bits/stdc++.h>
using namespace std;
int main(){
	string s, s1, s2;
	cin >> s;
	int n, a, b;
	cin >> n;
	while(n--){
		cin >> a >> b >> s1 >> s2;
		a--; b--;    //由于字符串数组的下标是从0开始,即需要减一
		string cp = s.substr(a, b-a+1), ss = s1 + s2;    //ss用来判断是否有插入位置的
		s.erase(a, b-a+1);    //将剪切位置的内容删掉
		int ii = s.find(ss);    //找插入位置是否存在
		if(ii == -1)    //如果没找到,s.find()返回-1
            s += cp;    //+:代表拼接字符串,s = s + cp;即将剪切板上的字符拼接到s后面
		else{    //如果找到,s.find()返回找到子串位置的第一个字符的地方
			string tmp = s.substr(0, ii);    //插入位置前的子串
			tmp += s1;    //将插入位置前的标志子串插入
			tmp += cp;    //将剪切的字符串插入
			tmp += s.substr(ii+s1.length(), s.length()-ii-s1.length()); //插入后面还剩下的
			s = tmp;
		}
	}
	cout << s << endl;
	
	return 0;
}

L1-10


int n,m;
int cnt;//统计做过的不简单的题个数
void solve(){
  cin>>n>>m;
    getchar();
    for(int i=0; i<n; i++)
    {
        string s;
        getline(cin,s);
        if(s.find("qiandao")==s.npos&&s.find("easy")==s.npos) //判断是否含有“easy"与"qiandao"
        {
            cnt++;
            if(cnt==m+1)//如果吉老师做到了已做过的题的下一个
            {
                cout<<s<<endl;//这是吉老师正在做的题
            }
        }
    }
    if(cnt<=m)//在这个范围内就AK;
        cout<<"Wo AK le\n";
}

L1-11

#include<bits/stdc++.h>
using namespace std;
char _map[1001][1001];//_map数组保存地图 
int flag[1001][1001],a[1000001];//a数组要开大一点,刚开始开a[1001]错了3个点 
//flag数组保存各个点所在的连通图,以及是否已经处理过,a数组保存各个连通图的大小
struct mg
{
    int x,y;
}q[1000001];
int main()
{
    int sx,sy,i,j,n,m,l,nx,ny,k,f,r,sum,d;
    int dx[4]={0,0,-1,1};
    int dy[4]={1,-1,0,0};//四个方向
    scanf("%d %d",&n,&m);//n是正方形地图边长,m是数据组数 
    memset(a,0,sizeof(a));
    memset(flag,0,sizeof(flag)); //你可以无视这两行memset
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            cin>>_map[i][j];//读入地图 
    d=0;//d用来保存当前是在第几个连通图中 
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(flag[i][j]==0)//如果当前位置不在已知连通图中(还未处理过) 
            {
                d++;//记录当前所在连通图数 
                f=1;
                r=1;
                q[f].x=i;
                q[f].y=j;
                flag[i][j]=d;
                sum=1;//初始化
                while(f<=r)
                {
                    for(k=0;k<4;k++)
                    {
                        nx=q[f].x+dx[k];
                        ny=q[f].y+dy[k];
                        if(flag[nx][ny]==0&&nx>=1&&nx<=n&&ny>=1&&ny<=n&&((_map[nx][ny]=='1'&&_map[q[f].x][q[f].y]=='0')||(_map[nx][ny]=='0'&&_map[q[f].x][q[f].y]=='1')))
                        //如果新位置能走且在地图上 
                        {
                            r++;
                            sum++;//计数器累加 
                            flag[nx][ny]=d;//标记新位置在第d个连通图中 
                            q[r].x=nx;
                            q[r].y=ny;//更新位置 
                        }
                    }
                    f++;
                }
                a[d]=sum;//保存当前连通图能移动到多少格 
            }
    for(i=1;i<=m;i++)
    {
        cin>>sx>>sy;//读入询问 
        cout<<a[flag[sx][sy]]<<endl;//直接查找答案并输出 
    }
    return 0;
}

L1-12

解题:
(1)第一部分输入的是 0-9 的数字,其中 0 表示我们已知的数字,然后依次输入 9 的数字,一定会有一个数字没有被输入,这个数字就是我们已知的数字的值;
(2)其次需要输入坐标,来刮数字,并且输出所刮数字;
(3)然后需要输入选择:1-3 表示行相加;4-6表示列相加;7表示主对角线相加;8表示副对角线;
(4)最后我们需要根据选择计算和,然后找到对应的金币。
易错点提示:
(1)记得把 0 替换为该位置的值:用一个数组 flag 来存数,输入的数标记为 1,没有输入的数标记为 0,寻找标记为 0 的数,即为已知的数;
(2)输入坐标的同时输出该坐标的数,只需要用二维数组来存放所有数字即可;
(3)输入选择并求和,用 if 把所有情况考虑到即可;
(4)换算金币时记得 sum - 6;

int n,m;
int a[5][5];
int b[5][5];
int c[10];
map<int,int>s;
void solve()
{  n=3;
s[6]=10000;
s[7]=36;
s[8]=720;
s[9]=360;
s[10]=80;
s[11]=252;
s[12]=108;
s[13]=72;
s[14]=54;
s[15]=180;
s[16]=72;
s[17]=180;
s[18]=119;
s[19]=36;
s[20]=306;
s[21]=1080;
s[22]=144;
s[23]=1800;
s[24]=3600;
		 for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
 		cin>>a[i][j];
		b[i][j]=a[i][j];
		c[a[i][j]]=1;
		}
 			for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++){ 
 				if(a[i][j]==0) {
 			for(int k=1;k<=9;k++) {
  			 	if(c[k]) continue;
   				b[i][j]=k;
   			break;
   								}
 							  }
							}
int ans=0;
for(int i=1;i<=3;i++){
			int z,y;
			cin>>z>>y;
			b[z][y]=a[z][y];
		cout<<b[z][y]<<endl;
}
cin>>n;
if(n<=3){
	for(int i=1;i<=3;i++) ans+=b[n][i];
}
else if(n<=6){
		for(int i=1;i<=3;i++) ans+=b[i][n-3];
}
else{
	if(n==7) { 
		ans+=b[1][1]+b[2][2]+b[3][3];
	}else
	 ans+=b[1][3]+b[2][2]+b[3][1];
}

 cout<<s[ans];
}

L1-13

const int N=100000;
struct node{
	int l,r;
}tr[N];
int n;
int m;
string s;
void solve()
{cin>>n>>m;
//等比数列求和公式,一共n行,每行是前一行二倍
n=(1-pow(2,n))/(1-2);
 while(m--){
 	 cin>>s;
 	  int u=1;//u所走的位置
    for(int i=0;i<s.size();i++){
    	if(s[i]=='y') u=u*2;//左下走
    	else u=u*2+1;//右下走
    }
//答案第(n+1)行下标-前n行总个数
cout<<u-n<<endl;
 }
	
}

L1-14

 根据题意可以进行三次分类

1、天梯赛成绩小于175的直接刷掉

2、天梯赛成绩大于等于175且pta成绩>=s,不用考虑有成绩重复的

3、天梯赛成绩大于等于175且pta成绩<s,把这个分数的人数统计一下,与批次s比较

int n,s,k,a[300];//用于记录同一个分数有多少人 
void solve(){
	cin>>n>>k>>s;
	int ans=0;
	while(n--){
		int t,p;
		cin>>t>>p;
		if(t>=175)//根据1,刷掉天梯赛成绩小于175的同学学 
		{
			if(p>=s)//根据3,天梯赛>=175&& pat>=s 
			ans++;
			else//根据3,记录天梯赛成绩为t的同学有多少人 
			{
				a[t]++;
			}
		} 
	}
	for(int i=175;i<=290;i++){
		ans+=min(a[i],k);//根据2,求出同一个分数段的同学有多少可以录用 
	}
	cout<<ans;
}

L1-15

题解(点击此处)

L1-999 (数论)  板子题

因为p不一定是质数,因此卡快速幂求逆元,

可以使用扩展欧几里得求逆元

线性递推求逆元

设 t = p / i,k = p % i,有:p = i * t + k
即 i * t + k Ξ 0 (mod p)
即 k Ξ - i * t (mod p)
两边同时除以 i * k
有 1 / i Ξ - t / k (mod p)
将k,t带入
有 inv[ i ] Ξ - p / i * inv[ p % i ] (mod p)
为防止有负数,有inv[ i ] = ( p - p / i * inv[ p % i ] % p ) % p
划重点:inv[ i ] = ( p - p / i * inv[ p % i ] % p ) % p
注意:【每个数对于模p的逆元不同】

  • exgcd和费马小定理只适合用来求单个逆元,求3e6以内所有的逆元肯定会超时,所以这里要用线性递推求逆元,这样可以保证时间复杂度在O( n )内
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define int long long
//扩展欧几里得求逆元
int exgcd(int a,int b,int &x,int &y){
	 if (!b) {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
int inverse(int a,int p){
	int x=0,y=0;
	int res=exgcd(a,p,x,y);
	if(res==1) res=(x+p)%p;
	else{res=-1;}
	return res;
}
int inv[3000010];
int l,r;
//线性递推求逆元
void incerse_e(int n,int p){
    int res=0;	
    inv[1] = 1;
    for(int i=2;i<=n;i++){
        inv[i] = p - (p / i * inv[p % i] % p) % p;
    }
    int q;
    cin>>q;
    while(q--) {
	res=0;
	cin>>l>>r;
    for(int i=l;i<=r;i++){
	res=(res%p+inv[i]%p)%p;
       }
        cout<<res<<endl;
    }
}
void solve(){
	int ok,n,p;
	cin>>ok>>n>>p;
	if(ok==1){
	int ti=inverse(n,p);
	if(ti==-1) cout<<"impossible\n";	
	else cout<<ti<<endl;
	return;
	}
	incerse_e(n,p);
}
signed main(){

	int t;cin>>t;
	while(t--){
		solve();
	}
	
	return 0;
}

L2-1

题解(点击此处)

L2-2

题解(点击此处)

L2-3

题解(点击此处)

L2-4

题解(点击此处)

L2-5(树形dp)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010, M = N * 2;// N个顶点至多2*N条有向边

int n;
int w[N];// 节点的权值
int h[N], e[M], ne[M], idx;
// h是顶点集,e[i]=b表示a指向b(一条边),ne[i]表示结点i的next的指针,idx指向当前需要插入(已经用过)的结点
LL f[N];// 在以u为根的子树中包含u的所有连通块的权值的最大值

void add(int a, int b)// 这里采用数组实现邻接表来存储图,也就是将多个单链表h[i]拼起来
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;// 头插法创建单链表,新节点指向已有节点
    // h[a]是单链表a的起点,最后一个插入元素的地址,也是idx区间的终点
}

void dfs(int u, int father)// 求f[i],第二个参数记录父节点,防止往回走
{
    f[u] = w[u];
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (j != father)
        {
            dfs(j, u);
            f[u] += max(0ll, f[j]);// long long的0,和0比较一下,如果<=0没必要加上
        }
    }
}

int main()
{
    scanf("%d", &n);
    memset(h, -1, sizeof h);// 记得h数组置-1

    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
    for (int i = 0; i < n - 1; i ++ )// 一共n-1条边
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);// 构建无向图
    }

    dfs(1, -1);

    LL res = f[1];
    for (int i = 2; i <= n; i ++ ) res = max(res, f[i]);// 求f[1]到f[n]的max

    printf("%lld\n", res);

    return 0;
}

L3-1

biset介绍

biset板子题(会用的话,纯签到)


#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll; 
#define int long long
int a[10010];
int n,m;
void solve()
{  cin>>n>>m;
vector<int>w;
for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n);
  int ans=0;
  for(int i=1;i<=n;i++){
  	int ti=ans+a[i];
	if(ans==m||ti>m) break;
  	  int tk=m-ti;
  	      bitset<(int)110> vis;//二进制
		//如果i+1到n全部的数据,任意凑数
		//可以凑出来X则vis[x]=1
            vis[0] = 1;
            for (int j = i+1; j <= n; j++) {
                vis |= vis << a[j];
            }
		
	if(vis[tk]) {w.push_back(a[i]);ans+=a[i];
}
  }
if(ans!=m) cout<<"No Solution";
else {
	for(auto k : w) {
		cout<<k;
if(k!=*(w.end()-1)) cout<<" ";
	}
}
}
signed main()
{
	ios::sync_with_stdio(false);
		cin.tie(0),cout.tie(0);

	solve();

	
	
	return 0;
}

爆搜YYDS:

因为M很小,才100,所以剪枝的作用很大,排序以后爆搜就行

注意特判一下这n个数的和与m的关系,如果小于m,则一定不可以凑出来,直接输出就行,不然会TLE在最后一个点

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k;
int tr[MAX];
vector<int>ans, a;
bool p;
void dfs(int id, int x){
    if(x > m || p || id > n + 1)return;
    if(x == m){
        p = 1;
        a = ans;
        return;
    }
    for(int i = id; i <= n; ++i){
        if(p)break;
        ans.push_back(tr[i]);
        dfs(i + 1, x + tr[i]);
        ans.pop_back();
    }
}

void work(){
    cin >> n >> m;
    int sum = 0;
    for(int i = 1; i <= n; ++i){
        cin >> tr[i];
        sum += tr[i];
    }
    if(sum < m){
        cout << "No Solution\n";
        return;
    }
    sort(tr + 1, tr + 1 + n);
    for(int i = 1; i <= n; ++i){
        if(tr[i] > m){
            n = i - 1;
            break;
        }
    }
    dfs(1, 0);
    if(!p)cout << "No Solution\n";
    else {
        for(int i = 0; i < a.size(); ++i){
            if(i)cout << " ";
            cout << a[i];
        }
        cout << endl;
    }
}


int main(){
    io;
    work();
    return 0;
}

「01背包 + 输出路径」

因为要字典序最小的,所以我们可以从大到小排个序,这样更新的时候是值小的后更新,输出的时候就是字典序最小的

dp[i][j]表示前i个,花费价值为j的钱能得到的最大价值

开一个vis[i][j]数组,为1则表示前i个物品,体积为j时可以选i这个物品使的价值得到最大,更新i的时候,判断一下dp[i][j]与dp[i-1][j - ar[i]]+ar[i]的大小,如果能更新的话,就让vis[i][j] = 1

输出路径的时候就倒着输出,看当前体积v和当前id的vis是否是1,是的话就选这个物品,并更新体积

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

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 10000 + 50
int n, m, k, x;
int tr[MAX];
int dp[MAX];
bool vis[MAX][105];
void work(){
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)cin >> tr[i];
    sort(tr + 1, tr + 1 + n, greater<int>());
    for(int i = 1; i <= n; ++i){
        for(int j = m; j >= tr[i]; --j){
            if(dp[j] <= dp[j - tr[i]] + tr[i]){
                dp[j] = dp[j - tr[i]] + tr[i];
                vis[i][j] = 1;
            }
        }
        
    }
    if(dp[m] == m){
        vector<int>ans;
        int id = n;
        while (m) {
            if(vis[id][m]){
                ans.push_back(tr[id]);
                m -= tr[id];
            }
            --id;
        }
        for(int i = 0; i < ans.size(); ++i){
            if(i)cout << " ";
            cout << ans[i];
        }
        cout << endl;
    }
    else cout << "No Solution\n";
}


int main(){
    io;
    work();
    return 0;
}

L4-1???

知识点

问题转移为: 有n个完全不同的球,分给m个不同的人

#include<iostream>
#include<bits/stdc++.h>
#include<math.h>
#define int long long
#define endl '\n'
const int N=1e6+5;
const int mod=1e9+7;
using namespace std;
int fz[N],fm[N];
int fpw(int x,int n)//快速幂
{
    int s=1;
    while(n)
    {
        if(n%2==1) s=s*x%mod;
        x=x*x%mod;
        n=n/2;
    }
    return s;
}
int inv(int x)//逆元
{
    return fpw(x,mod-2);
}
void intv()//初始化1~N阶乘的逆元
{
    int i,j;
    fz[0]=fm[0]=1;
    for(i=1;i<N;i++)
        fz[i]=fz[i-1]*i%mod;
    fm[N-1]=fpw(fz[N-1],mod-2);
    for(j=N-2;j>=1;j--)
        fm[j]=fm[j+1]*(j+1)%mod;
}
int c(int n,int k)//求C(n,k)
{
    if(n<k) return 0;
    return fz[n]*fm[k]%mod*fm[n-k]%mod;
}
signed main()
{
    int i,j,t,k,m,n;
    cin>>n>>m;
    intv();
    int sum=0;k=1;
    for(i=0;i<=m;i++)
    {
        if(i%2==0)
        sum+=c(m,i)*fpw(m-i,n)%mod;
        else
        sum+=mod-c(m,i)*fpw(m-i,n)%mod;//如果sum直接加上-c(m,i)*fpw(m-i,n)%mod会有可能变为负数,所以加上个mod保证其为正数
        sum=sum%mod;
    }
    sum=sum*fm[m]%mod;
    cout<<sum<<endl;
}

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值