今天下午嘗試了下CometOJ的歡樂賽,好吧,是對那些大佬來說是歡樂賽,其中出了9道題,5個小時,我只A了2道,其中有三道都是超時,所以最後也沒做出了,關於題目,比賽完只搞定了其中5題,覺得是自己比賽應該可以搞定的。
B 距離產生美
這道簽到題我也花了半個小時(黑臉),這道其實關鍵在於選擇合適的貪心算法,就是每次更新變量的時候需要判斷它前後兩個變量的值,我們的貪心思想是:儘量選擇更新中間那個變量,這樣可以使得所需要更新得變量儘可能地少,即是題目要求
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const long long inf = 1e18;
inline int read(){
char ch = getchar(); int x = 0, f = 1;
while(ch< '0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main(){
int n=read(),k=read();
long long a[maxn];
int ans = 0;
for(int i=0;i<n;i++)
a[i] = read();
for(int i=1;i<n;i++){
if(fabs(a[i]-a[i-1]) < k){
if(fabs(a[i+1]-a[i])<k){
a[i] = inf;
ans++;
}else{
a[i-1] = inf;
ans++;
}
}
}
printf("%d\n",ans);
return 0;
}
C 烤麪包片
這道題原來我以爲卡的是時間和數據量,對於階乘來說1e9的數據是真的太大了,這樣的一般複雜度是O(1),但比賽的時候我沒有反應過來,超時了一直以爲是算法不夠優化。嘗試各種算法,包括二分求階乘,還有通過二進制來計算大數據量,像下面這個,兩數相乘,通過將其中一個數化爲二進制,利用按位與來對數相乘不斷取模,從而不會爆數據。
其中還想到用二分遞歸,但數據量真的太大啦,這點優化完全不濟於事。所以輸入大的數據的會發現出現程序動不了了。
這題的真正解決思路是:首先根據範圍可得 4!!一定大於109 (事實上甚至大於1018),所
以 4!!!一定能被 mod 整除(n>4 也同理)。那麼我們只需要判斷 n≤3
的情況了。直接計算即可。如果寫一個階乘函數,然後調用連續三次
來獲得答案,如:((n!%mod)!%mod)!%mod 可能會無法通過以下的 text:
2 2
(本應輸出 0,但三次調用階乘函數最終會輸出 1,因爲第一次調用
得到 0,之後再調用階乘函數發現 0!是 1)
一個較好的處理方式是將 n=0,1,2 特判掉,然後預先計算出 3!!爲
720,然後用一次 for 循環解決這道題。
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n,mod,ans=1;
cin >> n >> mod;
if(n==0)
return cout << 1%mod,0;
if(n<3)
return cout << n%mod,0;
if(n>3)
return cout << 0,0;
for(int i=1;i<=720;i++)
ans = (ans*i)%mod;
return cout << ans,0;
}
G 籃球校賽
這道題比賽的時候沒有用任何特殊的操作或者是剪枝就直接遞歸,雖然能算出答案,但導致程序計算時計算了很多沒有用的量,耗費了大量的時間,然後我就卡在時間上,比賽過後,看着大佬的題解:
大佬是在存入數據的時候就按能力值從1-5排序更新每一個位置,並把這些數據存入到一個二維數組中,在調用dfs遞歸的時候,就根據這個二維數組進行遞歸和計算,節省了很多時間,但有些人說可以用五次循環直接暴力得出,這個我還沒有嘗試。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+2;
const int N = 5;
ll a[maxn][N+1],ans=0;
bool vis[maxn];//記錄是否取某個球員
ll mmax[N+1][N+1];
void dfs(int cur,ll temp){
if(cur == N+1){
ans = max(ans,temp);
return;
}
for(int i=1;i<=N;i++){
if(vis[mmax[i][cur]])
continue;
vis[mmax[i][cur]] = true;
dfs(cur+1,temp+a[mmax[i][cur]][cur]);
vis[mmax[i][cur]] = false;
}
}
int main(){
std::ios::sync_with_stdio(false);
int n;
cin >> n;
for(int i=1;i<=n;i++){
for(int j=1;j<=N;j++){
cin >> a[i][j];
for(int pos=1;pos<=N;pos++){ // pos在該能力的排位
if(a[i][j] > a[mmax[pos][j]][j]){
for(int k=N;k>=pos+1;k--)
mmax[k][j] = mmax[k-1][j];//將這些元素後移一位
mmax[pos][j] = i;//插入
break;
}
}
}
}
dfs(1,0);
cout << ans << endl;
return 0;
}
G 三元組
出題人思路:對於一組滿足條件的(i,j):
2 ∗ min(?? + ??,?? + ??) ≤ max(?? + ??,?? + ??)
也必然滿足:
min(ai + aj,?? + ??) ≤ max(?? + ??,?? + ??)
不妨設 2(?? + ??) ≤ ?? + ??,即ai + aj ≤ ?? + ??
此時,顯然不需要考慮min(?? + ??,?? + ??) = ?? + ??這個條件
移項可得 (2 ∗ ?? − bi) + (2 ∗ ?? − bj) ≤ 0
直接按照2 ∗ ?? − ??升序進行排序,記數組爲P
此時可以發現性質:
若??最多隻能和? ?匹配,則?? + 1只能和[i + 1,j]之間的匹配
此時使用滑窗即可計算答案
又因爲我們只考慮了2(?? + ??) ≤ bi + bj的情況
再對每個三元組中的??,bi進行交換,計算一次答案即可。
時間複雜度O(n + nlog2 ?)
//三元組
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
const ll mod = 1e9+7;
int n;
ll sum[maxn];
struct node{
ll a,b,c;
}p[maxn];
bool cmp(node x,node y){
return (2*x.a-x.b) < (2*y.a-y.b);
}//比較函數
ll solve(){
sort(p+1,p+n+1,cmp);
sum[0] = 0;
for(int i=1;i<=n;i++)
sum[i] = (sum[i-1]+p[i].c)%mod;//求前綴和
int l=1,r=n,sign=0;
ll k = 2*p[1].a - p[1].b;
while(l <= r){
int mid = (l+r)>>1;
ll t = 2*p[mid].a - p[mid].b;
if((t+k) <= 0)
sign = mid,l = mid+1;
else
r = mid-1;
}//求出最右邊下標
ll res = 0;//two pointer
for(l=1,r=sign;l<=r;l++){
while(l<=r && (2ll*p[l].a + 2ll*p[r].a - p[l].b - p[r].b) > 0)
r--;
if(l > r)
break;
ll temp = (sum[r]-sum[l-1]+mod)%mod;
res = (res + p[l].c * temp % mod)%mod;
}
return res;
}
int main(){
std::ios::sync_with_stdio(false);
cin >> n;
for(int i=1;i<=n;i++)
cin >> p[i].a >> p[i].b >> p[i].c;
ll ans = solve();
for(int i=1;i<=n;i++)
swap(p[i].a,p[i].b);
ans += solve();
ans %= mod;
cout << ans << endl;
return 0;
}
I: Gree 的心房
這道簽到題也卡了我一會,原來是數據量卡了我,沒有用long long,最後幾個數據過不了,這題的思路就是求最短路,關於最短路,在這裏我們可以知道沿着邊緣從左上角走到右下角最短,即(n-1)+(m-1),然後關於障礙物,當取得最短路的時候,障礙物最多可以放(n-1)(m-1)個,因爲障礙物是隨機放的,也沒要求我們輸出障礙物位置,我們就可以直接判斷,其負責度爲O(1)。
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
long long ans = n+m-2;
if(k > (n*m)-ans-1)
printf("-1");
else
printf("%lld",ans);
return 0;
}
題外話
今天還是很自閉的,畢竟一直卡着,5個小時只做出2題,哎,還是自己做的題目不夠多,太菜了。也感覺最近的生活有些累,還是得好好緩一下,加油!晚安。