2020-6-6模擬賽題解

前言

這篇題解大概是我 8:008:00 寫的,當時感覺自己 AK\texttt{AK} 了。9:009:00 這篇題解已經出爐了,檢查了一下代碼沒發現啥問題,最後一題不會對拍,自閉了,如何生成一棵樹啊。。。

正文

T1

題目描述

小x有一個整數,他想將該數各個位上數字反轉得到一個新數。新數也應滿足整數的常見形式,即除非給定的原數爲零,否則反轉後得到的新數的最高位數字不應爲零。他覺得這個問題太簡單,就將其拋給了你。

題目分析

開始的時候就開始開 T1T1,直接寫完交了

模擬一下就好了

題目代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
int main(){
	int n,ans=0;read(n);
	if(n<0){putchar('-');n*=-1;}
	while(n){
		ans=ans*10+n%10;
		n/=10;
	}cout<<ans;
	return 0;
}

T2

題目描述

​ 小x學了編程,但他吃了飯沒事幹,於是想自己寫一個計算器,計算器上有“+”、“-”、“*”、“/”(整除號)的按鍵,但他寫完以後發現他寫錯了,看着幾百行的程序,他心態崩了,不想調,於是讓你給他寫一個計算器,來處理一堆正整數的加減乘除運算。

題目分析

T2T2 是個模擬題,思路大家應該都清楚,先把數字和符號隔離出來,然後先把乘法和除法算好,次數式子就變成了只有加法和減法的式子,這個沒有難度吧。

題目代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
vector<pair<int,char> >q;
vector<pair<int,char> >s;
int work1(){
	for(unsigned i=0;i<q.size();i++){
		switch(q[i].second){
			case '+':s.push_back(q[i]);break;
			case '-':s.push_back(q[i]);break;
			case '*':s[s.size()-1].first*=q[i].first;break;//算乘
			case '/':s[s.size()-1].first/=q[i].first;break;//算除
		}
	}
	int ans=0;
	for(unsigned i=0;i<s.size();i++){
		if(s[i].second=='+')ans+=s[i].first;
		else ans-=s[i].first;
	}return ans;
}
int work(){
	string st;
	cin>>st;
	q.push_back(make_pair(0,'+'));//第1個數前面的符合可以看成是+號
	for(unsigned i=0;i<st.size();i++)
		if(isdigit(st[i]))q[q.size()-1].first=q[q.size()-1].first*10+st[i]-48;
		else q.push_back(make_pair(0,st[i]));
	cout<<work1()<<endl;
	return 0;
}
int main(){
	int T;
	read(T);
	while(T--){
		q.clear();s.clear();
		work();
	}
	return 0;
}

T3

題目描述

小x有一張由n行和m列組成的圖片。行從上到下從1到n編號,列從左到右從1到m編號。每個單元都塗成黑色或白色。

小x認爲這幅畫不夠有趣。如果一幅畫裏至少有一個十字架,你會覺得它很有趣。十字由一對數字x和y表示,其中1≤x≤n和1≤y≤m,這樣x行中的所有單元格和y列中的所有單元格都塗成黑色。
例如,這些圖片中的每個都包含十字架:

以下圖像不包含十字架:

你有一把刷子和一罐黑漆,所以你可以使這幅畫有趣。每分鐘你都可以選擇一個白色的單元格,然後把它塗成黑色。問題是你最少需要多少次,才能使圖片至少包含一個十字?

題目分析

先枚舉行,算行需要多少次塗鴉牆,然後看列。

注意一個小細節,行和列有重合的地方。

題目代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
char ch[1010][1010];
int s[1010];
int work(){
	int n,m,ans=INT_MAX;read(n);read(m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>ch[i][j];
	for(int j=1;j<=m;j++)
		for(int i=1;i<=n;i++)
			if(ch[i][j]=='.')s[j]++;
	for(int i=1;i<=n;i++){
		int sm=0,mx=INT_MAX;
		for(int j=1;j<=m;j++)
			if(ch[i][j]=='.')sm++;
		for(int j=1;j<=m;j++)
			mx=min(mx,s[j]-(ch[i][j]=='.'));//這裏!
		ans=min(ans,mx+sm);
	}cout<<ans<<endl;
	return 0;
}
int main(){
	int T;read(T);
	while(T--){
		memset(s,0,sizeof(s));
		work();
	}
	return 0;
}

T4

題目描述

小x自娛自樂,他在黑板上寫下了一行共n個數字,分別是1,2k,22k…2(n-1)k。現在,他想先擦去黑板上第1個數字,然後擦去黑板上剩餘數字中的第2個數字,再擦去剩餘數字中第3個數字……直到小x無法繼續擦去。現在你想知道,算法停止後第x個剩餘數的值是多少。

題目分析

首先,這個擦數會令我們感到不爽,於是,我們看看會取哪些數字。

假設有 1010 個數:{1,2,3,4,5,6,7,8,9,10}\{ 1,2,3,4,5,6,7,8,9,10\}

  • 11,序列變成 {2,3,4,5,6,7,8,9,10}\{ 2,3,4,5,6,7,8,9,10\}
  • 33,序列變成 {2,4,5,6,7,8,9,10}\{ 2,4,5,6,7,8,9,10\}
  • 55,序列變成 {2,4,6,7,8,9,10}\{ 2,4,6,7,8,9,10\}
  • 77,序列變成 {2,4,6,8,9,10}\{ 2,4,6,8,9,10\}
  • 99,序列變成 {2,4,6,8,10}\{ 2,4,6,8,10\}
  • 無法繼續進行,操作結束

此時很明顯的規律如下:所有的奇數會被擦掉,所有的偶數會被留下。

再拓展一下,最後剩下的數的個數 == 偶數的個數 == n2\lfloor \frac{n}{2} \rfloor

這樣的話就簡單了。

首先判斷 1-1,直接看 n2\lfloor \frac{n}{2} \rfloor 是否 << xx

然後第 xx 個數,顯然等於 2(x21)k2^{(x*2-1)k}

題目代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
void write(ll x){
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
const int mod=19260817;
ll pw(ll x,ll y){
	ll ans=1;
	while(y){
		if(y&1)ans=ans*x%mod;
		y>>=1;
		x=x*x%mod;
	}return ans;
}
int work(){
	ll n,x,k;read(n);read(x);read(k);
	n/=2;
	if(x>n)return puts("-1"),0;
	write(pw(2,(x*2ll-1ll)*k));
	puts("");
	return 0;
}
int main(){
	int T;read(T);
	while(T--){work();}
	return 0;
}

T5

題目描述

小x管理着N 個城市,這些城市之間有N-1條道路將他們連接起來,小x發現任何兩個城市都能夠直接或者間接通過道路連接。
雖然所有城市是可以互相到達的,但是他擔心有恐怖分子破壞這片區域的安定,破壞道路,於是小x又祕密地修建了 M 條小路來連接。
果不其然,最近一個奇怪的組織意欲破壞道路,請你幫小x想想辦法,如果城市 A 到城市 B 的直連道路被破壞,從 A 走到 B 的所有路徑中,經過小路的距離最少是多少?

題目分析

nn 個點,n1n-1 條邊,整張圖連通,這不是樹嗎?

首先,樹有一個性質——砍掉任意的一條邊,就不聯通。

我們以一張圖爲例。

  • 橙色的邊表示刪除的邊。
  • 紅色的邊表示樹上的邊。
  • 紫色的邊表示新加的邊。

先說一個結論,如果有解的話,新加的邊用且僅用一次

爲什麼?

首先一條邊被刪除後,一棵樹變成了兩棵樹,起點和終點必定在不同的樹裏,所以我們需要一座橋連接兩棵樹。

那爲什麼不會使用多條新加的邊呢?因爲在一棵樹內,任意兩點均能相互達到。

知道這個就好辦了。

我們先 dfsdfs 找連通塊,遍歷其中一棵子樹。

我們再去找這棵子樹連向另外一棵子樹的新加邊,並從中找到邊權最小的作爲我們的答案。

題目代碼

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
const int MAXN=1e4+10;
int n,m,x[MAXN],y[MAXN],h[MAXN];
vector<int>v[MAXN];
vector<pair<int,int> >e[MAXN];
vector<int>v1;
void dfs(int x,int fa,int z){
	v1.push_back(x);h[x]=1;
	for(auto i:v[x])
		if(i!=fa&&i!=z)dfs(i,x,z);
}
int main(){
	read(n);read(m);
	for(int i=1;i<n;i++){
		read(x[i]),read(y[i]);
		v[x[i]].push_back(y[i]);
		v[y[i]].push_back(x[i]);
	}
	for(int i=1;i<=m;i++){
		int x,y,z;read(x);read(y);read(z);
		e[x].push_back(make_pair(y,z));
		e[y].push_back(make_pair(x,z));
	}
	for(int i=1;i<n;i++){
		v1.clear();memset(h,0,sizeof(h));
		dfs(x[i],0,y[i]);
		int ans=INT_MAX;
		for(auto j:v1)
			for(auto k:e[j])
				if(!h[k.first])ans=min(ans,k.second);
		if(ans==INT_MAX)puts("-1");
		else cout<<ans<<endl;
	}
	return 0;
}

時間複雜度

這個代碼剛寫完時,我嚇了一跳,這不是 O(n2m)O(n^2m) 嗎?

過了一會兒,我想了想,發現這其實是 O(nm+n2)O(nm+n^2) 的,而且跑不滿。

爲什麼?

我們看 353735\sim 37 行代碼,這裏並不是 O(nm)O(nm) 的,而是 O(m)O(m) 的,因爲新加的一條邊最多被訪問 22 次(22 個端點各訪問一次),時間複雜度是忽略常數的,並且這裏是真的跑不滿

但願這題不 TLETLE (要 TLETLE 也只會因爲那個 memsetmemset 可能 TT),不 WAWA(沒道理 WAWA),不 RERE(沒資格 RERE),不 UKEUKE(沒可能 UKEUKE),不 MLEMLEvectorvector 幫我定着呢),不 CECE(這個網站開 c++11,我試過)。(所以不可能不 ACAC !但願吧。。。)

後記

由於這篇題解是比賽的時候寫的,所以分數暫時還不知道,但願能做對的都做對,最好能 AKAK

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章