NWPU周赛题解

太久没做题了,做题做出来就是没感觉。

要么就是知道做,写不对;要么就是看不懂题,看懂了之后觉得很简单

哈夫曼树的简单模拟都不会了必须得使用C++的优先队列搞法,简直弱爆炸

————————————————————————————————————————————————————————————————

废话不多说,先来一发比赛链接

NWPU开学第一战


————————————————————————————————————————————————————————————————

A题是字符串的处理题似乎要用到kmp

C题是图论的题,暂时没思路先留空留在这,会了再说


————————————————————————————————————————————————————————————————

B题:

exgcd的基本题

注意题意的要求有两个:系数和尽量小,总砝码质量也尽量小

exgcd

上列了个exgcd的基本题链接,可以看到弱的代码


基本算法思想就不解释了,百度一大堆,注意这个题的方法。必须考虑到三种情况:

ax+by=m

-ax+by=m

ax-by=m

然后,就简单了


ll a,b,m,ansx,ansy,tot;
ll x,y,g,l,r,x1,x2;

ll exgcd(ll a,ll b,ll &x,ll &y){
	if (a==0&&b==0) return -1;
	if (b==0){
		x=1;y=0;return a;
	}
	ll d=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return d;
}

int main(){
	//input;
	while(scanf("%I64d%I64d%I64d",&a,&b,&m)!=EOF){
		if (a+b+m==0) break;
		tot=ansx=ansy=INF;
		tot*=99999999;

		g=exgcd(a,-1*b,x,y);
		x*=m/g;
		y*=m/g;
		l=b/g;if (l<0) l=-1*l;
		r=a/g;if (r<0) r=-1*r;
		x1=(x%l+l)%l;
		y=(y%r+r)%r;
		x2=(m+b*y)/a;
		for(x=x1;x<=x2;x+=l){
			y=(a*x-m)/b;
			if (y<0) continue;
			if (x+y<=ansx+ansy&&tot>=x*a+b*y){
				ansx=x;
				ansy=y;
				tot=x*a+b*y;
			}
		}
		
		g=exgcd(a,b,x,y);
		x*=m/g;
		y*=m/g;
		l=b/g;if (l<0) l=-1*l;
		r=a/g;if (r<0) r=-1*r;
		x1=(x%l+l)%l;
		y=(y%r+r)%r;
		x2=(m+b*y)/a;
		for(x=x1;x<=x2;x+=l){
			y=(m-a*x)/b;
			if (y<0) continue;
			if (x+y<=ansx+ansy&&tot>=x*a+b*y){
				ansx=x;
				ansy=y;
				tot=x*a+b*y;
			}
		}
		
		
		m=-1*m;
		g=exgcd(a,-1*b,x,y);
		x*=m/g;
		y*=m/g;
		l=b/g;if (l<0) l=-1*l;
		r=a/g;if (r<0) r=-1*r;
		x1=(x%l+l)%l;
		y=(y%r+r)%r;
		x2=(m+b*y)/a;
		for(x=x1;x<=x2;x+=l){
			y=(a*x-m)/b;
			if (y<0) continue;
			if (x+y<=ansx+ansy&&tot>=x*a+b*y){
				ansx=x;
				ansy=y;
				tot=x*a+b*y;
			}
		}
		printf("%I64d %I64d\n",ansx,ansy);
	}
	return 0;
}

————————————————————————————————————————————————————————————————

D题:

熟悉前缀和的,熟悉欧拉函数的,这个题就是个模板咯

直接上代码也能懂的

可以直接打表的随便搞法


LL euler[maxn+50];

void getEuler(){
	memset(euler,0,sizeof(euler));
	euler[1]=1;
	for(int i=2;i<=1000000;i++)
		if (!euler[i])
			for(int j=i;j<=1000000;j+=i){
				if (!euler[j]) euler[j]=j;
				euler[j]=euler[j]/i*(i-1);
			}
}

int main(){
	//freopen("input.txt","r",stdin);
	getEuler();
	for(int i=3;i<=maxn;i++)
		euler[i]+=euler[i-1];
	int n;
	//printf("%I64d\n",euler[maxn]);
	while(scanf("%d",&n)!=EOF){
		if (!n) break;
		printf("%I64d\n",euler[n]);
	}
	return 0;
} 

————————————————————————————————————————————————————————————————

E题:

看到题的数据量很开心,50*50的矩阵,可以随便搞

看懂题了之后觉得很恶心,怎么处理连通

找到了一个通法:dfs中,由从A格去B格的方向为基础判断

如:A去B的时候,方向为往右走,那么A的开口是必须有右的,B的开口是必须有左的,同理其他三个方向

(说是这么说还是得注意细节,我是用结构体,存储每个节点的开口)

那么这个题就变成了处理联通块个数的问题了(会dfs一定会这种题吧,嗯嗯,一定是)

唯一1A的题,麻烦的题就会尽力注意细节了


int n,m,ans;
char mp[maxn][maxn];
int col[maxn][maxn];

int dx[]={0,1,-1,0,0};
int dy[]={0,0,0,1,-1};

struct node{
	int u,l,r,d;
}Node[maxn][maxn];

void getdir(char ch,node &x){
	x.u=x.l=x.r=x.d=0;
	if (ch=='A') x.u=x.l=1;
	else if (ch=='B') x.u=x.r=1;
	else if (ch=='C') x.d=x.l=1;
	else if (ch=='D') x.d=x.r=1;
	else if (ch=='E') x.u=x.d=1;
	else if (ch=='F') x.l=x.r=1;
	else if (ch=='G') x.u=x.l=x.r=1;
	else if (ch=='H') x.u=x.d=x.l=1;
	else if (ch=='I') x.l=x.r=x.d=1;
	else if (ch=='J') x.r=x.u=x.d=1;
	else x.u=x.r=x.d=x.l=1;
	//printf("%d %d %d %d\n",x.u,x.r,x.l,x.d);
}

bool check(int x,int y,int nx,int ny,int dir){
	if (dir==1&&Node[x][y].d&&Node[nx][ny].u) return true;
	if (dir==2&&Node[x][y].u&&Node[nx][ny].d) return true;
	if (dir==3&&Node[x][y].r&&Node[nx][ny].l) return true;
	if (dir==4&&Node[x][y].l&&Node[nx][ny].r) return true;
	return false;
}

void dfs(int x,int y){
	for(int k=1;k<=4;k++){
		int nx=x+dx[k];
		int ny=y+dy[k];
		if (nx>=1&&nx<=n&&ny>=1&&ny<=m&&!col[nx][ny])
			if (check(x,y,nx,ny,k)){
				col[nx][ny]=ans;
				dfs(nx,ny);
			}
	}
}

int main(){
	//freopen("input.txt","r",stdin); 
	while(scanf("%d%d",&n,&m)!=EOF){
		ans=0;
		if (n==-1&&m==-1) break;
		memset(col,0,sizeof(col));
		for(int i=1;i<=n;i++) scanf("%s",mp[i]+1);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				getdir(mp[i][j],Node[i][j]);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				if (!col[i][j]){
					ans++;
					col[i][j]=ans;
					dfs(i,j);
				}
		printf("%d\n",ans);
	}
	return 0;
}

————————————————————————————————————————————————————————————————

F题:

直接让我想退役不想玩了的题

想做模拟的方法竟然不会写了

看完题,关键词any,就是说每次算完两个小的数之和再次排序再次取两个小的,那么:哈夫曼!

直接优先队列搞一发,每次取最小的两个呗


写了个优先队列的基本使用方法以及优先级的定义:

优先队列学习戳我


priority_queue<int,vector<int>,greater<int> > q;

int main(){
	//freopen("input.txt","r",stdin);
	int n,num,ans,num1,num2;
	while(scanf("%d",&n)!=EOF){
		ans=0;
		while(!q.empty()) q.pop();
		for(int i=1;i<=n;i++){
			scanf("%d",&num);
			q.push(num);
		}
		for(int i=1;i<n;i++){
			num1=q.top();
			q.pop();
			num2=q.top();
			q.pop();
			num=num1+num2;
			ans+=num;
			q.push(num);
		}
		printf("%d\n",ans);
	}
	return 0;
}

————————————————————————————————————————————————————————————————

G题:

数学想法题

所谓阶乘,需要去找到特殊的数:质数

要求数位最多,那么尽可能拆分才会最多:分解质因数

但是质数没法拆分:知道了2,3,5,7的处理方法

4,6,8,9怎么办?

尽可能用小的

一定要注意阶乘:9!=7!*8*9=7!*(2*2*2)*(3*3)=7!*3!*3!*2!

那么每个数字单独搞就好

细节:(wa了几发):n的范围:int存不下

直接字符串搞就好了的


int num[20];
int n,x;
char s[20];
int main(){
	//freopen("input.txt","r",stdin);
	while(scanf("%d",&n)!=EOF){
		scanf("%s",&s);
		memset(num,0,sizeof(num));
		for(int i=0;i<n;i++){
			x=s[i]-'0';
			if (x==2) num[2]++;
			else if (x==3) num[3]++;
			else if (x==4) num[3]++,num[2]+=2;
			else if (x==5) num[5]++;
			else if (x==6) num[5]++,num[3]++;
			else if (x==7) num[7]++;
			else if (x==8) num[7]++,num[2]+=3;
			else if (x==9) num[7]++,num[2]++,num[3]+=2;
		}
		for(x=7;x>=2;x--)
			if (num[x])
				while(num[x]){
					printf("%d",x);
					num[x]--;
				}
		puts("");
	}
	return 0;
}

————————————————————————————————————————————————————————————————

总结:

简单题需要保证好细节的1A

难题需要慢慢找回来节奏去学

不管怎么样,祝周末的蓝桥杯顺利

你可能感兴趣的:(源代码,ACM)