November 26, 2019

2019 SWJTU-ACM新秀杯 预赛 题解

屏幕在深夜微微发亮/ 思想在那虚树路径上彷徨/ 平面的向量交错生长/ 织成 忧伤的网

2019 SWJTU-ACM新秀杯 预赛 题解

T1 取数字

描述

给定两个长度分别为m和n的、仅由数字1-9组成的字符串。现从这两个字符串中取出总共k个数字,拼接成一个数,要求从同一个字符串中取出的数字保持其在原字符串中的相对顺序。求满足上述条件的最大的数。

输入

第一行一个整数T (1≤T≤100),表示T组数据。
每一组数据包括3行。前两行是字符串,最后一行是一个数字k。字符串长度m、n满足1≤m,n≤303; k满足1≤k≤m+n。

输出

输出共T行,包括T个数

思路

显然n和m都不大,可以枚举第一个串取i个数,另外一个串取k-i个数,对每个字符串单独取数字找到最大的数,再把这两个数拼起来就得到了取i、k-i个数字的情况下最大的数。

对于每个字符串取i个数时,从左往右扫,每次删去非严格递减序列的最后一个数,直到剩下i个数为止。

代码
#include<cstdio>
#include<iostream>
#include<cstring> 
using namespace std;
const int maxn=700;
char a[maxn],b[maxn],c[maxn],d[maxn],u[maxn],la[maxn];
int n,m;
void sel1(int x){
	int p=n-x;              //要删的数目
	int i=0,j=-1;        
	while(a[i]){
		while(j>=0&&p&&c[j]<a[i]){
			j--;
			p--;
		}
		j++;c[j]=a[i];i++;
	}
	c[x]='\0';
	return ;
}

void sel2(int x){
	int p=m-x;
	int i=0,j=-1;
	while(b[i]){
		while(j>=0&&p&&d[j]<b[i]){
			j--;
			p--;
		}
		j++;d[j]=b[i];i++;
	}	
	d[x]='\0';
	return ;
}

void mer(int k){
	int t=0;
	int v1=0,v2=0;
	while(c[v1]&&d[v2]){
		u[t++]=strcmp(c+v1,d+v2)>0?c[v1++]:d[v2++];	
	}
	if(c[v1]){
		while(c[v1]){u[t++]=c[v1++];}	
	}
	else {
		while(d[v2]){u[t++]=d[v2++];}
	}
	int ok=0;
	for(int i=0;i<k;i++){
		if(u[i]>la[i]){
			ok=1; break;
		}
		else if(u[i]<la[i]){
			ok=0;break;	
		}
	}
	if(ok){
		for(int i=0;i<k;i++){
			la[i]=u[i];
		}
	}
	return ;
}

int main(){
	freopen("data.in","r",stdin);
	freopen("aaa.out","w",stdout);
	int T,k;
	scanf("%d",&T);
	while(T--){
		memset(c,'\0',sizeof(c));
		memset(d,'\0',sizeof(d));
		memset(u,'\0',sizeof(u));
		memset(la,'\0',sizeof(la));
		scanf("%s",a);
		scanf("%s",b);
		scanf("%d",&k);
		n=strlen(a);m=strlen(b);
		for(int i=0;i<=k;i++){
			memset(c,'\0',sizeof(c));
			memset(d,'\0',sizeof(d));
			if(i>n||k-i>m) continue;
			sel1(i);
			sel2(k-i);
			mer(k);
		}
		for(int i=0;i<k;i++){
			printf("%c",la[i]);	
		}
		printf("\n");
	}	
}
 

T2 爱吃蛋糕的小生生

Description

我们都知道,小生生十分爱吃蛋糕,今天小生生参加了一个晚会,发现晚会里有n(1≤n≤10e8)个蛋糕,小生生发现蛋糕从左到右排序分别为1号,2号……n1号,2号……n号,晚会的主人小江江告诉小生生第i号蛋糕的价值为i!元,同时他会选择一些编号的蛋糕提问小生生,如果小生生能答对对应编号的蛋糕的价值能被2整除多少次,小生生就可以吃该编号的蛋糕。小生生十分想吃蛋糕,可是他的算术能力不行,现在,他想让聪明的你帮他解答一下小江江的问题。

Input

多组输入,每组第一行一个正整数T(1≤T≤100),表示小江江一共选择了T个蛋糕。接下来T行,每行表示小江江选择蛋糕的一个编号T=0时结束输入。保证输入输入的总蛋糕数量不大于10000。

Output

T行,每行一个整数,表示该编号蛋糕价值能被2整除的次数

思路

我们发现,对于数m,从从m/2+1到m数的乘积一共可以整除2的次数为m/2次,因此利用递归,就可以算出来了。

代码
#include<cstdio>
#include<iostream>
using namespace std;
int ke(int n){
       if(n==0) return 0;
       else return ke(n/2)+n/2;
}
int main(){
	int T;
	while(scanf("%d",&T)){
		if(T==0)break;
		while(T--){
			int x;scanf("%d",&x);	
			printf("%d\n",ke(x));			
		}
	}
}

T3帮他数数

描述

小A是一个卡牌游戏玩家,他现在手上有n张战斗力不同的卡片。某一天他遇到了一个任务,需要他组合出一个特定的战斗力x才能进行,然后他来寻求你的帮助。这个问题对你来说很简单,你不但能告诉他能否组出来,还能明确地告诉他有多少种方法可以完成这个组合。告诉你他每张卡的战斗力和目标战斗力c,请告诉他有多少种不同的组合方式。

输入

多组数据输入,第一行包含一个整数T(1≤T≤10),表示有T组数据。对于每组数据:第一行包含两个整数n (1≤n≤100),k(1≤k≤2000),表示小A有n张卡片,他带着k个问题来找你。第二行包含n个整数ci,表示第i张卡的战斗力为c[i],保证所有c[i]的和不大于2000。第三行包含k个整数xi,表示他的第i个问题的目标战斗力x。

输出

对于每组数据,输出k行,每行包含一个整数,问题的答案对1000000007取模后的结果。

思路

背包方案数

代码

T4 不想上课

代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
    int T; cin >> T;
    while (T--){
        int n, m, k; cin >> n >> m >> k;
        bool ans = false;
        while (k--){
            int x, y; cin >> x >> y;
            if(x<=5 || x>=n-4 || y<=5 || y>=m-4)
                ans = true;
        }
        cout << (ans?"YES":"NO") << endl;
    }
    return 0;
}

T5 Chess Board

描述

XX有一个大小为n行m列的棋盘,但这块棋盘的格子上面不小心染上了许多不同的颜色。XX有强迫症:他希望自己的棋盘上只有一种颜色,所以他决定从原来的n行m列的棋盘上切下一块矩形的小棋盘。同时他又不想过于浪费,所以他想保证这块小棋盘的面积尽可能的大。请你帮他解决这个问题。(切割时不能破坏棋盘上每个格子的完整性)

输入

第一行一个整数T,表示测试样例个数(T≤10)。对于每个测试样例,第一行包含两个整数n,m,分别表示棋盘的行数、列数(1≤n∗m≤1e6)。接下来n行,每行一个长度为m的只包含数字(0到9)的字符串。第i行字符串中的第j个数字表示棋盘上第i行j列格子的颜色。

输出

输出一个整数,XX能切下来的小棋盘的最大面积。

思路

最大子矩阵问题,悬线法

代码

T6数 (签到题)

代码
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
struct node{
	long long x,y,z;
}a[7];
int main(){
	int T;
	a[1].x=1;a[1].y=2;a[1].z=3;
	a[2].x=1;a[2].y=3;a[2].z=2;
	a[3].x=2;a[3].y=1;a[3].z=3;
	a[4].x=2;a[4].y=3;a[4].z=1;
	a[5].x=3;a[5].y=1;a[5].z=2;
	a[6].x=3;a[6].y=2;a[6].z=1;
	scanf("%d",&T);
	while(T--){
		long long c[4];
		scanf("%lld%lld%lld",&c[1],&c[2],&c[3]);
		long long maxx=c[1]+c[2]+c[3];
		for(int i=1;i<=6;i++){
			maxx=max(maxx,c[a[i].x]+c[a[i].y]+c[a[i].z]);
		}
		for(int i=1;i<=6;i++){
			maxx=max(maxx,c[a[i].x]*c[a[i].y]+c[a[i].z]);
		}
		for(int i=1;i<=6;i++){
			maxx=max(maxx,(c[a[i].x]+c[a[i].y])*c[a[i].z]);
		}
		for(int i=1;i<=6;i++){
			maxx=max(maxx,(c[a[i].x]*c[a[i].y])*c[a[i].z]);
		}
		printf("%lld\n",maxx);
	}
}

T7 以这样的点名有什么用呢

思路

找规律

代码
#include<cstdio>
#include<iostream>
using namespace std;
int a[1000+10][1000+10];
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		int n,m,xx,yy;
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				scanf("%d",&a[i][j]);
				if(a[i][j]==-1){
					xx=i;yy=j;	
				}
			}
		}
		int ans;
		if(xx==1&&yy==1){
			cout<<(-1)*(a[2][2]-a[1][2]-a[2][1])<<endl;
		}
		else cout<<(-1)*(a[xx-1][yy-1]-a[xx][yy-1]-a[xx-1][yy])<<endl;
	}
}

T8 菜市场

描述

炉石传说中有两张卡牌,奴隶主和旋风斩。以下是两张卡的卡牌介绍:
奴隶主:奴隶主有3滴血,每当他受到伤害且不死亡就会重新生成一个新的奴隶主(3滴血)。
旋风斩:对所有随从造成1点伤害。
戴java老师是有名的炉石天才,它能够在谈笑风生间计算出场上剩余奴隶主的个数,炉石小白onsterm也想知道结果,请你帮帮他。

输入

输入有两行,第一行n,m分别代表奴隶主的个数和旋风斩的个数(1≤n≤10e5,0≤m≤10e9)。第二行n个数,a_i第i个奴隶主的剩余血量。(1≤a_i≤3)

输出

输出连续打出m个旋风斩后,剩余奴隶主的个数(结果对1000000007取模)。

思路

矩阵快速幂,设f[i]为血量为i的奴隶主个数,则一次旋风斩后有:f[1]=f[2],f[2]=f[3],f[3]=f[1]+f[2]。

代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#define ll long long
#define gc() getchar()
#define mo 1000000007
using namespace std;
int n;
struct ahaha{
    ll a[4][4]; 
    ahaha(){
        memset(a,0,sizeof a);
    }
    void build(){     
        for(int i=1;i<=4;++i)a[i][i]=1;
    }
}a,b,c;
ahaha operator *(const ahaha &x,const ahaha &y){
    ahaha z;
    for(int k=1;k<=3;++k)
        for(int i=1;i<=3;++i)
            for(int j=1;j<=3;++j)
                z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%mo)%mo;
    return z;
}
ll k;
void init(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
    	int xl;
        scanf("%d",&xl);
        b.a[1][xl]++;
    }
}

int main(){
    init();
    ahaha ans;ans.build();
    a.a[3][3]=1;
    a.a[2][1]=1;
    a.a[2][3]=1;
   	a.a[3][2]=1;
    while(k){
        if(k&1)ans=ans*a;
        a=a*a;k>>=1;
    }
	c=b*ans;
	/*
	for(int i=1;i<=3;i++){
    	for(int j=1;j<=3;j++){
			cout<<ans.a[i][j]<<" ";
		}
		cout<<endl;
    }
    */
	ll num=c.a[1][1]+c.a[1][2]+c.a[1][3];
	printf("%lld",num);
    return 0;
}