2019爪哇部落第十届新生选拔赛 题解

博采众长,共同进步

  • A.空军十一号 阅读题 筛选信息 送气球
  • B.小爪的子阵和 贪心 最大连续字段和的二维升级版
  • C.爪爪逃逸 模拟+思维
  • D.小爪的三视图 模拟 立方体 暴力+思维
  • E.爪哇的路 最短路 加强理解 dijkstra+堆优化
  • F.爪哇神话 三维区间dp 石子合并的加强版范围合并
  • G.部落队形 字符串 马拉车manacher算法
  • H.部落外的树 线段树or树状数组or差分
  • I.爪爪口袋中的斐波那契 数论 大数类+推规律
  • J.爪哇之谜 数论 斐波那契前缀和 非质数取模
  • K.爪爪的九宫图 搜索 BFS+哈希
  • L.小爪的道路 二分答案
  • M.小爪的Party 并查集+优先队列 思维题

  注:按照以往经验,难度评价以ACM区域赛作为标准

 

A.空军十一号

考点:送气球题,阅读筛选信息

思路:根据题目信息,疯狂if…else即可

#include
#include
using namespace std;
 
int main(){
    int a1,b1,c1,a2,b2,c2;
    cin>>a1>>b1>>c1>>a2>>b2>>c2;
 
    if(a1>a2)
        cout<<1<b2)
            cout<<2<c2)
                cout<<2<

 B.小爪的子阵和

考点:经典题,贪心

  • 这道题目是hdu1003 的升级版,hdu1003是一维数组最长子段和的问题,这个题目扩展到二维,思路就是把二维转换成一维
  • 先求第一行最大子段和,再求第一行跟第二行合起来的最大子段和,再求第一行到第三行合起来的最大值,实际上就是把二维数组转换成一维的了
#include 
using namespace std;
#include  //memset
#define INF 0x3f3f3f3f
const int SZ=102;
 
int d[SZ][SZ];
int s[SZ];
 
//最大子段和
int MaxArray(int a[],int n)
{
	int m=-INF;
	int tmp=-1;
	for(int i=0;i0)
			tmp+=a[i];
		else
			tmp=a[i];
		if(tmp>m)
			m=tmp;
	}
	return m;
}
 
int main()
{
	int i,j,k,n;
	cin>>n;
	for(i=0;i>d[i][j];
 
	int ans=-INF,tmp;
	for(i=0;ians)
				ans=tmp;
		}
	}
	cout<

C.爪爪逃逸

思路:思维题,以m为中心统计右边L的个数sum1和左边R的个数sum2,min(sum1,sum2)就是ans

#include 
#include 
#include 
using namespace std;
#define MAX 100005
char str[MAX];

int main()
{
    int t;
    int n,m,sum1,sum2;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&n,&m);
        scanf("%s",str+1);
        sum1=0;
        sum2=0;
        for(int i=m;i1;i--)
            if(str[i]=='R')
            sum2++;
        printf("%d\n",min(sum1,sum2));
    }
    return 0;
}

 D.小爪的三视图

考点:暴力+思维

思路: 题意就是给你三视图,让你求体积。数据中给出了该图形的三视图,若还原的立体图形中,某一位有是实体,在三视图中,三个位置都应该出现过。所以此时暴力枚举每个小块,如果都为1,那么就ans++;这样就可以不重不漏的暴力出答案,这题数据规模也很小,只有100,适应暴力。难点更多的应该在英文理解上。

#include
#define ll long long 
#define fo(i,j,n) for(register int i=j; i<=n; ++i)

using namespace std;
const int N = 105;
int a[N][N], b[N][N], c[N][N];
int main(){
		
		int A,B,C;
	while(scanf("%d%d%d",&A,&B,&C)!=EOF){
		fo(i,1,A){
			fo(j,1,B){
				cin>>a[i][j]; 
			}
		}
		fo(i,1,B){
			fo(j,1,C){
				cin>>b[i][j];
			}
		}
		fo(i,1,C){
			fo(j,1,A){
				cin>>c[i][j];
			}
		}
		int ans = 0;
		fo(i,1,A){
			fo(j,1,B){
				fo(k,1,C){
					if(a[i][j]&&b[j][k]&&c[k][i]){
						ans++;
					}
				}
			}
		}
		cout<

E.爪哇的路

图论 最短路 dijkstra+堆优化

#include
#define ll long long 
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N = 1005,M=N*4;
const long long mod = 998244353;
int head[N],Next[M],ver[M],edge[M],tot;
void add(int x, int y, int z){
	ver[++tot]=y,edge[tot]=z;
	Next[tot]=head[x], head[x]=tot; 
} 
ll d[N];
bool vis[N];
int D[N];
void dij(int st){
	memset(d, 0x3f, sizeof(d));
	memset(vis, 0, sizeof(vis)); 
	memset(D, 0, sizeof(D));
	priority_queue > q; //  这个要注意 
	q.push({0,st});
	d[st] = 0;
	while(q.size()){
		int x = q.top().second;q.pop();
		if(vis[x])continue;
		vis[x] = 1;
		for(int i=head[x]; i; i=Next[i]){
			int y = ver[i], w = edge[i];
			if(d[x] + w < d[y]){
				d[y] = d[x] + w;
				if(x==st)D[y]=0;
				else D[y] = max(D[x], x);
				q.push({-d[y], y});
			}else if(d[x] + w == d[y]){
				int tmp = 0;
				if(x==st)tmp = 0;
				else tmp = max(x, D[x]); // 到达x的必要D[x] 
				if(tmp < D[y]){  // 可以有更好的选择 
					D[y] = tmp;
					q.push({-d[y], y});
				}
			}
		}
	}
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		memset(head, 0, sizeof(head));
		tot = 0;
		int n, m, x, y, z;
		scanf("%d %d",&n,&m);
		fo(i,1,m){
			scanf("%d%d%d", &x, &y, &z);
			add(x,y,z);
			add(y,x,z);
		}
		ll ans = 0;
		fo(i,1,n){
			dij(i);
			fo(j,1,n){
				ans = (ans+D[j])%mod;
			}
		}
		printf("%lld\n",ans);
	}
	return 0; 
}

F.爪哇神话

三维区间dp,dp[i][j][k]表示以i为开头j为结尾的区间分为k份需要的最少花费,dp[i][j][k]=min(dp[i][j][k],dp[i][p][k-1]+dp[p+1][j][1]),dp[i][j][1]=dp[i][j][k]+sum[j]-sum[i-1](k>=l&&k<=r)

#include
#include
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n,L,R,a[105],sum[105];
int dp[105][105][105],inf = 0x3f3f3f3f;
int main(){
	while(scanf("%d%d%d",&n,&L,&R)!=EOF){
		fo(i,1,n){
			scanf("%d",&a[i]);
			sum[i] = sum[i-1] + a[i];
		}	
		memset(dp, 0x3f, sizeof(dp));
		fo(i,1,n)dp[i][i][1]=0;
		for(int r=1; r<=n; r++){ // 区间长度 
			for(int i=1; i+r-1<=n; ++i){
				int j = i+r-1; 
				dp[i][j][r] = 0; // 无合并
				for(int k=r; k>=1; k--){  // 枚举堆数 
					if(k==1){
						for(int z=L; z<=R; z++){
							dp[i][j][1] = min(dp[i][j][1], dp[i][j][z]+sum[j]-sum[i-1]);
						}
					}else{
						for(int z=i; z

 G.部落队形

考点:字符串 manacher马拉车算法 

题意:给定n给整数,求最长回文串,并且前缀非递减,后缀非递增

题解:套manacher模版

#include
#include 
#include
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N=1e5+5;
int T,n,a[N];
int Ma[N*2],Mp[N*2],ans;
int Manacher(int a[]){
	int l = 0;
	Ma[l++]=1;
	Ma[l++]=1000;
	fo(i,1,n){
		Ma[l++]=a[i];
		Ma[l++]=1000; 
	}
	Ma[l]=2;
	int mx=0,id=0;
	for(int i=0; ii?min(Mp[2*id-i],mx-i):1;
		int last = Ma[i];
		while(Ma[i+Mp[i]]==Ma[i-Mp[i]] && (Ma[i+Mp[i]]==1000||Ma[i+Mp[i]]<=last)){
			if(Ma[i+Mp[i]]!=1000)last=Ma[i+Mp[i]];
			Mp[i]++;	
		}
		if(i+Mp[i]>mx){
			mx = i+Mp[i];
			id=i;
		}
		ans = max(ans,Mp[i]);
	}
	return ans-1;
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		fo(i,1,n)scanf("%d",&a[i]);
		ans=0;
		printf("%d\n",Manacher(a));
	}
	return 0;
}

 H.部落外的树

这题是很裸的线段树题了,不过线段树代码量长可以考虑用差分or树状数组

这里展示的是差分的做法

#include
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N=1e6+5;
int d[N],n,m; 
void add(int l, int r, int x){
	d[l] += x;
	d[r+1] -= x;  
}
int main(){
		int t;scanf("%d",&t);
		while(t--){
			scanf("%d%d",&n,&m);
			n++; // 从0开始 
			add(1,n,1);
			int l,r;
			fo(i,1,m){
				scanf("%d%d",&l,&r);
				l++,r++; 
				add(l,r,-1);
			}
			int ans = 0; 
			fo(i,1,n){
				d[i] += d[i-1];
				if(d[i]>0)ans++;
			}
			printf("%d\n",ans);
			memset(d,0,sizeof(d));
		} 
	return 0;
}

I.爪爪口袋中的斐波那契

考点:大数类+推规律 

大数题建议使用Java或者Python做,其他题首先考虑c++

首先需要知道的知识点:斐波那契数列前n项之和等于F[n+2]-1,不信可以试试?

知道上面的知识点,考场上联想到这规律,就很好推规律了

问题转移成:前b项的和-前a-1项的和,使用上述规律,即(F[b+2]-1)-(F[a+2-1]-1)

所以只需要判断F[b+2]-F[a+1]的奇偶性

写出前几项的奇偶性就能推出啦~~

2019爪哇部落第十届新生选拔赛 题解_第1张图片

import java.math.BigInteger;
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int T = in.nextInt();
		
		while(T-->0) {
			BigInteger x = in.nextBigInteger().add(BigInteger.valueOf(1));
			BigInteger y = in.nextBigInteger().add(BigInteger.valueOf(2));
			int a=1,b=1;
			if(y.mod(BigInteger.valueOf(3)).equals(BigInteger.valueOf(0)))
				b=0;
			if(x.mod(BigInteger.valueOf(3)).equals(BigInteger.valueOf(0)))
				a=0;
			if((a==1 && b==1) || (a==0 && b==0))
				System.out.println(0);
			else
				System.out.println(1);
		}
		

	}

}

 J.爪哇之谜

 考点:又是一道数论题

###斐波那契前缀和 
因为乘积满足幂加的关系
容易知道要求幂的斐波那契数列 
然后要求前n项和可知是斐波那契前缀和 
又斐波那契前缀和与与后面第二项的差-1相等
重点是C^D这一项的幂....
观察规律可知是 fa+fb-n
fa为a的幂
fb为b的幂 
(f*A*n) =  [fib(n), fib(n+1)]下标0开始 
n:        1 2 3 4 5 6  7     (以下表示幂)
B        0 1 1 2 3 5  8 13....这行是 Fib...
前缀和  0 1 2 4    7 12 20     sumA(n-1) = f(n+1)-1 = (f*A*n)[1]-1 下标从0开始

A       1 0 1 1 2 3  5  8
前缀和  1 1 2 3 5 8  13  sumB(n-1) = f(n) = (f*A*n)[0]

C^D     0 0 1 2 4 7  12
前缀和  0 0 1 3 7 14 26  sumC(n-1) =  sumA(n-1) + sumB(n-1) - n

#include 
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
ll a,b,c,d,mod,n,x,MOD;
int q_pow(ll a, ll n){
	int ret = 1;
	while(n){
		if(n&1)ret = ret*a%mod;
		a = a*a%mod;
		n>>=1;
	}
	return ret%mod;
}
int phi(int n){
	int ans = n;
	for(int i=2; i<=sqrt(n); i++){
		if(n%i==0){
			ans = ans/i*(i-1);
			while(n%i==0)n/=i;
		}
	}
	if(n>1)ans = ans/n*(n-1);
	return ans;
}
void mul(int f[2], int a[2][2]){
	int c[2];
	memset(c,0,sizeof c);
	for(int j=0; j<2; j++){
		for(int k=0; k<2; k++){
			c[j] = (c[j] + (ll)f[k]*a[k][j])%MOD;
		}
	}
	memcpy(f,c,sizeof(c));
}
void mulself(int a[2][2]){
	int c[2][2];
	memset(c,0,sizeof(c));
	for(int i=0; i<2; i++){
		for(int j=0;j<2; j++){
			for(int k=0; k<2; k++){
				c[i][j] = (c[i][j]+(ll)a[i][k]*a[k][j])%MOD;
			}
		}
	}
	memcpy(a,c,sizeof(c));
}
void solve(){
	int f[2] = {0,1};
	int A[2][2] = {{0,1},{1,1}};
	int t = n; 
	for(;t;t>>=1){
		if(t&1)mul(f, A);
		mulself(A);
	} 
//	cout<<"fib:"<>a>>b>>c>>d>>mod>>n){
		MOD = phi(mod);
		solve();
	}
	return 0;
}

K.爪爪的九宫图

简单的搜索题bfs+哈希,和普通搜索题不一样的是需要用哈希表来代替标记数组 

#include 
#include 
#include
#include
 
using namespace std;
char start[4][4],goal[4][4];
int df[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
map vis;
struct Node
{
    int x,y;
    long long step;
    char Map[4][4];
};
bool check_g(Node a)///判断这个图和目标图是否相同
{
    for(int i=1;i<=3;i++)
     for(int j=1;j<=3;j++)
      if(a.Map[i][j]!=goal[i][j])return false;
    return true;
}
bool check_cf(Node a)///判断这个局面是否出现过
{
    string s = "";
    for(int i=1;i<=3;i++)
     for(int j=1;j<=3;j++)s+=a.Map[i][j];
    if(vis[s]>0)return false;
    vis[s]++;
    return true;
}
int BFS(int x1,int y1)///BFS寻找最少步数
{
    Node cur,next;
    cur.x = x1;cur.y = y1;
    cur.step = 0;
    for(int i=1;i<=3;i++)
      for(int j=1;j<=3;j++)
        cur.Map[i][j] = start[i][j];//复制一份状态
    queue Q;//创建一个队列
    Q.push(cur); //加入队列
    if(check_g(cur)) return cur.step;//检查是否为目标状态
    while(!Q.empty()){
        cur = Q.front();
        Q.pop();    // 出队列
        for(int i=0;i<4;i++){
            next.x = cur.x + df[i][0];
            next.y = cur.y + df[i][1];
             for(int i1=1;i1<=3;i1++)
              for(int j=1;j<=3;j++)
               next.Map[i1][j]= cur.Map[i1][j];//复制一份状态
            next.step = cur.step + 1;//当前 步数加一
            if(next.x>=1&&next.x<=3&&next.y>=1&&next.y<=3){//边界内
                swap(next.Map[next.x][next.y],next.Map[cur.x][cur.y]);///交换两个方格的位置;
                if(check_cf(next)){//检查重复
                    if(check_g(next)) return next.step;//检查是否为目标状态
                   Q.push(next);//入队列
                }
            }
        }
    }
    return -1;
}
int main()
{
    int x1,y1;
    for(int i=1;i<=3;i++)
      for(int j=1;j<=3;j++){
            cin>>start[i][j];
            if(start[i][j]=='.') x1=i,y1=j;///记录空白格的位置
        }
    for(int i=1;i<=3;i++)
       for(int j=1;j<=3;j++)
            cin>>goal[i][j];
    cout<

 L.小爪的道路

考点:二分答案

二分答案与二分查找类似,二分答案主要通过二分可行解来寻找最优解。
如:有一个可行解x=4 ,那么最优解只存在于4~max(最大可能解)
如果中间值mid = (max+x)/2, mid可行的话,最优解存在于 mid~max
如果mid不可行,最优解存在于4~mid

#include
#include
#include
#define maxn 1000000000
using namespace std;
int l,m,n;
int num[50005];
bool check(int cnt){
    int last = 0, sum = 0;
    for(int i = 1; i <= m+1; i++){
        if(num[i] - num[last] < cnt){
            sum++;
        }else{
            last = i;
        }
    }
    if(sum <= n)
        return 1;
    else
        return 0;
}

int main(){
    int ans;
    scanf("%d %d %d",&l,&m,&n);
    int left = 1, right = l;
    for(int i = 1; i <= m; i++){
        scanf("%d",&num[i]);
    }
    num[m+1] = l;
    while(left <= right){
        int mid = (right-left)/2+left;      //防越界
        if(check(mid)){         //如果可行
            ans = mid;
            left = mid+1;
        }
        else{
            right = mid-1;
        }
    }
    printf("%d\n",ans);
    return 0;
}

 M.小爪的Party

考点:大家最喜欢的并查集,优先队列

题意:有n个人编号从1-n,m对朋友关系,当一个人入场时发现场内没有认识的朋友就会不高兴,首先输出最小不高兴值,然后输出字典序最小的入场顺序使得n个人的不高兴值最小。

这题是比较考思维的,希望大家能给惊喜~~A掉TA!!

思路:要使不高兴值最小,那么当入场一个人p,那么p的朋友要跟着入场,p的朋友的朋友也要跟着入场,这样就能使得不高兴值最小,只有1。很容易想到用并查集判断n个人中有几个不相关的集合,即为最小的不高兴值。为使字典序最小,很容易想到用优先队列维护当前最小可入场的人的编号。注意p进去了并不代表p的朋友的朋友可以进去,必须看到直接的朋友才会高兴,所以每次进去一个人,只把他的所有直接朋友压入优先队列。在每次取优先队列的头元素时要注意判断剩余集合的最小元素是否有比头元素字典序小的,考虑到这一点我们在连接两个人时优先让字典序比较小的成为父节点。

#include
#include
#include
#include
#include
using namespace std;

const int MAX = 1e6+5;
int n,m,x,y;
int a[MAX],b[MAX],f[MAX];
bool vis[MAX];
vector g[MAX];

int find(int x){
    if(f[x]==x)
        return x;
    return f[x] = find(f[x]);
}

void Merge(int x,int y){
    int a = find(x);
    int b = find(y);
    if(a!=b){
        if(a>b)
            f[a] = b;
        else
            f[b] = a;
    }
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            a[i] = i;
            f[i] = i;
            vis[i] = false;
            g[i].clear();
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            g[y].push_back(x);
            Merge(x,y);
        }
        priority_queue, greater > pq;
        int t=0,ans=0;
        for(int i=1;i<=n;i++)
        if(f[i]==i){
            pq.push(a[i]);
            vis[i] = true;
            ans++;
        }

        while(!pq.empty()){
            int u = pq.top();
            pq.pop();
            b[++t] = u;
            for(int i=0;i

 

你可能感兴趣的:(算法)