NWPU周赛题解
太久没做题了,做题做出来就是没感觉。
要么就是知道做,写不对;要么就是看不懂题,看懂了之后觉得很简单
哈夫曼树的简单模拟都不会了必须得使用C++的优先队列搞法,简直弱爆炸
————————————————————————————————————————————————————————————————
废话不多说,先来一发比赛链接
————————————————————————————————————————————————————————————————
A题是字符串的处理题似乎要用到kmp
C题是图论的题,暂时没思路先留空留在这,会了再说
————————————————————————————————————————————————————————————————
B题:
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
难题需要慢慢找回来节奏去学
不管怎么样,祝周末的蓝桥杯顺利