武漢工程大學計算機科學與工程學院第二屆程序設計新生賽 解題報告
xzc 2020.3.13
鏈接:武漢工程大學計算機科學與工程學院第二屆程序設計新生賽 <–
序言:
這套題質量還不錯。可以感覺到出題人的用心設計。考察了乘法逆元,快速冪,並查集,dp,線段樹等知識點。雖然對於新生來說可能大部分人還沒有學過這些知識點,但是都考察得很基礎。這種比塞題對於後續的學習還是很有益處的。
A. Hello, algorithm(86/175)
分析:
我們想AC,當然是輸出“AC”了。
代碼:
#include <bits/stdc++.h>
using namespace std;
int main()
{
cout<<"AC\n";
return 0;
}
B. 等閒變卻故人心(30/106)
樣例輸入:
3 3
...
.*.
樣例輸出:
4
分析:
簡單bfs,從起點開始bfs,最後一個入隊的點即爲最遠的點。從起點到這個點的步數即爲所求。
代碼:
#include <bits/stdc++.h>
using namespace std;
const int N = 1003;
char a[N][N];
bool vis[N][N];
int m,n;
struct Node{
int x,y,step;
Node(int xx=0,int yy=0,int s=0):x(xx),y(yy),step(s){}
}node;
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
void bfs()
{
int x,y,xx,yy,step;
queue<Node> Q;
Q.push(Node(1,1,0));
vis[1][1] = true;
int ans = 0;
while(!Q.empty())
{
node = Q.front();Q.pop();
x = node.x, y = node.y, step = node.step;
ans = step;
for(int i=0;i<4;++i)
{
xx = x+dx[i], yy = y+dy[i];
if(vis[xx][yy]||a[xx][yy]!='.') continue;
Q.push(Node(xx,yy,step+1));
vis[xx][yy] = true;
}
}
cout<<ans<<endl;
}
int main()
{
cin>>n>>m;
memset(a,'*',sizeof(a));
for(int i=1;i<=n;++i)
scanf("%s",a[i]+1),a[i][m+1]='*';
bfs();
return 0;
}
C. Rie(1/4)
分析:
這題是模擬一個簡單編譯器。比賽的時候1/4,沒做,跳過了。
D. 所有的相遇,都不過是離別的序曲(5/32)
分析:
這題覺得有規律,但是不好找。應該就是個結論題吧。我本來想dp遞推一下,推個幾百天說不定就夠精度了,後來好像也不保險,而且多組輸入,複雜度也太高。這題應該說是個概率的結論吧,答案就是min(1,p/(1-p))
代碼:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T;
double p,q;
cin>>T;
while(T--)
{
scanf("%lf",&p);
q = 1-p;
printf("%.6f\n",min(1.0,p/q));
}
return 0;
}
E. 夜雨江湖無故舊(5/15)
樣例輸入:
1
233 2333 23333 233333
樣例輸出:
2322931
分析:
按照題意,求x = a^b, y = c^d, x乘以y關於mod的逆元即可。
代碼:
#include <bits/stdc++.h>
using namespace std;
const int mod = 20190930;
typedef long long LL;
LL fast_pow(LL a,LL b,LL m)
{
if(a==b) return 0;
LL ans = 1;
while(b)
{
if(b&1) ans = ans*a%m;
a = a*a%m;
b>>=1;
}
return ans;
}
void exgcd(LL a,LL b,LL& d,LL&x,LL&y)
{
if(b==0) {
d = a; x = 1; y = 0;return;
} exgcd(b,a%b,d,y,x);
y -= (a/b)*x;
}
LL getNi(LL a,LL b)
{
LL d,x,y;
exgcd(a,b,d,x,y);
return (x%b+b)%b;
}
int main()
{
LL a,b,c,d,t;
cin>>t;
while(t--)
{
cin>>a>>b>>c>>d;
a = fast_pow(a,b,mod);
c = fast_pow(c,d,mod);
c = getNi(c,mod);
a = a*c%mod;
cout<<a<<endl;
}
return 0;
}
F. 朋友圈(11/59)
樣例輸入:
5 2
1 3
3 5
樣例輸出:
3 1 3 1 3
分析:
用並查集維護朋友關係,然後輸出每個人所在集合的人數即可。
代碼:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int pre[maxn],cnt[maxn];
void initUFset(int n)
{
for(int i=1;i<=n;++i)
pre[i] = i;
}
int Find(int x)
{
return x==pre[x]?x:pre[x]=Find(pre[x]);
}
void join(int x,int y)
{
int fx = Find(x), fy = Find(y);
if(fx!=fy) pre[fy] = fx;
}
int main()
{
int n,m,x,y;
cin>>n>>m;
initUFset(n);
while(m--) {
scanf("%d%d",&x,&y),
join(x,y);
}
for(int i=1;i<=n;++i)
++cnt[Find(i)];
for(int i=1;i<=n;++i)
printf("%d%c",cnt[Find(i)],i==n?'\n':' ');
return 0;
}
G. 肥宅快樂水(66/301)
樣例輸入:
2 4 5
樣例輸出:
60
分析:
顯然,求所有人數的最小公倍數,然後乘以n即可。lcm(a,b) = a * b / gcd(a,b)
, gcd可以用歐幾里得原理求,也可以用g++的__gcd()函數
。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL gcd(LL a,LL b)
{
return (b==0)?a:gcd(b,a%b);
}
LL lcm(LL a,LL b)
{
return a/gcd(a,b)*b;
}
int main()
{
LL a,b,c;
cin>>a>>b>>c;
a = lcm(a,b);
a = lcm(a,c);
cout<<a*3<<endl;
return 0;
}
H. 跳格子(11/24)
樣例輸入:
2
6 2 3
1 2 3 4 5 6
5 3 3
1 2 3 4 5
樣例輸出:
11
-1
分析:
顯然是dp。dp[x] = max(dp[x-s],dp[x-(s+1)],dp[x-(s+1)], ... , dp[x-t])+v[x]
。
代碼:
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int v[N],dp[N],s,t;
int main()
{
int T,n,s,t;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&s,&t);
for(int i=1;i<=n;++i)
scanf("%d",&v[i]);
memset(dp,-1,sizeof(int)*(n+1));
dp[1] = v[1];
for(int i=2;i<=n;++i)
{
for(int j=s;j<=t;++j)
{
if(i<=j||dp[i-j]==-1) continue;
dp[i] = max(dp[i],dp[i-j]+v[i]);
}
}
printf("%d\n",dp[n]);
}
return 0;
}
I. 大煉丹師(73/172)
樣例輸入:
4
3 3 3
3 4 5
2 2 3
1 2 3
樣例輸出:
equilateral
right
normal
error
分析:
可以先對邊長排序,這樣好寫。
代碼:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int a[3],T;
scanf("%d",&T);
while(T--) {
for(int i=0;i<3;++i)
scanf("%d",a+i);
sort(a,a+3);
if(a[0]+a[1]<=a[2]) {
puts("error");
} else if(a[0]==a[2]) {
puts("equilateral");
} else if(a[0]*a[0]==a[2]*a[2]-a[1]*a[1]) {
puts("right");
} else {
puts("normal");
}
}
return 0;
}
J. 辭舊迎新(26/180)
樣例輸入:
helloworld
reverie
樣例輸出:
eeeeirorld
分析:
貪心即可。對t排序,對字符串s,從前往後,如果比t當前最小的字符大,就替換掉。
代碼:
#include <bits/stdc++.h>
using namespace std;
char s[100005],t[100005];
int main(void) {
int lens,lent;
scanf("%s%s",s,t);
lens = strlen(s), lent = strlen(t);
sort(t,t+lent);
int p = 0;
for(int i=0;i<lens&&p<lent;++i)
if(s[i]>t[p]) s[i] = t[p++];
puts(s);
return 0;
}
K. 麥田守望者(2/17)
樣例輸入:
5 3
1 2 3 4 5
2 1 5
1 2 4
2 1 5
樣例輸出:
3
5
分析:
顯然可以用線段樹維護。我們用線段樹維護區間最大值。題目是單點更新,所以不需要lazy標記。
查詢區間最大值的時候,直接從線段樹的根節點往下查即可。查詢區間中第一個大於給定值的位置時,我們可以先從線段樹根節點往下遞歸,若在查詢區間範圍內,優先查詢左子樹。
代碼:
#include <bits/stdc++.h>
#define Lson left,mid,pos<<1
#define Rson mid+1,right,pos<<1|1
using namespace std;
const int N = 5E5+3;
const double e = 2.7182818;
int a[N],Max[N<<2];
int build(int left,int right,int pos) {
if(left==right) return Max[pos] = a[left];
int mid = (left+right)/2;
return Max[pos] = max(build(Lson),build(Rson));
}
int modify(int left,int right,int pos,int x,int val) {
if(left>x||right<x) return Max[pos];
if(left==right) return a[left] = Max[pos] = val;
int mid = (left+right)/2;
return Max[pos] = max(modify(Lson,x,val),modify(Rson,x,val));
}
int query(int left,int right,int pos,int qL,int qR)
{
if(qL>right||qR<left) return -1000000000;
if(qL<=left&&right<=qR) return Max[pos];
int mid = (left+right)/2;
return max(query(Lson,qL,qR),query(Rson,qL,qR));
}
int Query(int left,int right,int pos,int qL,int qR,int v)
{
if(left>qR||right<qL||Max[pos]<=v) return 10000000; //不在所求的範圍之內
if(left==right) return left;
int mid = (left+right)/2;
if(qL<=left&&right<=qR)
{
if(Max[pos<<1]>v) return Query(left,mid,pos<<1,qL,qR,v);
else return Query(mid+1,right,pos<<1|1,qL,qR,v);
}
int idL = Query(left,mid,pos<<1,qL,qR,v);
int idR = Query(mid+1,right,pos<<1|1,qL,qR,v);
return min(idL,idR);
}
int main()
{
int n,q,x,v,l,r,op,m,Lmax,Rmax,id;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)
scanf("%d",a+i);
build(1,n,1);
while(q--)
{
scanf("%d",&op);
if(op==1) {
scanf("%d%d",&x,&v);
modify(1,n,1,x,v);
} else {
scanf("%d%d",&l,&r);
if(l==r) {
printf("%d\n",a[r]);continue;
} m = l+floor(1.0*(r-l+1)/e);
Lmax = query(1,n,1,l,m);
Rmax = query(1,n,1,m+1,r);
if(Rmax<=Lmax) id = r;
else id = Query(1,n,1,m+1,r,Lmax);
printf("%d\n",a[id]);
}
}
return 0;
}
2020/3/13
13:58
xzc