【解題報告】CSP-S2020
【解題報告】CSP-S2020
從這一年開始NOIP和CSP正式變為四道題目,兩天的考試已經成為歷史了
T1 儒略日
思路
當年這道題目把我噁心死了,導致我沒辦法去NOIP2020
這道題目按理來說就是模擬,中間要處理一下格里高利曆的空出的日期,以及公元前和公元后的一些閏年的處理,還有一些月份的處理
時至今日,我還是不想打模擬題
於是暗示了我的完美結局
實際上我們可以預處理四百年的資訊,再來根據這個資訊稍微改變以及推算一下就好了
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> #define int long long using namespace std; const int maxn=146097; int q; int y[maxn],m[maxn],d[maxn]; int r,tmp; int calc(int year,int month) { if(month==2) { if(year%4!=0) return 28; else { if(year%100!=0) return 29; else { if(year%400!=0) return 28; else return 29; } } } if(month==4||month==6||month==9||month==11) return 30; else return 31; } signed main() { m[0]=d[0]=1; for(int i=1;i<maxn;i++) { d[i]=d[i-1]+1; m[i]=m[i-1]; y[i]=y[i-1]; if(d[i]>calc(y[i],m[i])) { m[i]++,d[i]=1; } if(m[i]>12) { y[i]++; m[i]=1; } } cin>>q; while(q--) { cin>>r; if(r>2299160) { r-=2159351; tmp=r/maxn*400+1200; r%=maxn; } else { tmp=r/1461*4-4712; r%=1461; } cout<<d[r]<<" "<<m[r]<<" "; if(tmp+y[r]>0) cout<<tmp+y[r]; else cout<<(1-tmp-y[r])<<" BC"; cout<<'\n'; } return 0; }
T2 動物園
思路
位運算
甚至比T1還要簡單
我們直接用一個 unsigned long long
來儲存所有的飼料要求
然後對於所有的剩下的沒有選的動物的編號跟要求進行對比
然後我們對於每個要求,進行一次操作
我們繼續觀察發現,既然每個動物都有的話,我們實際上要知道的就是飼料清單上第 \(p_i\) 位上為 \(0\) 的數目
實際上答案就是 \(2^{k-S}-n\)
注意 \(2^{64}\) 需要特判一下
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> #define int unsigned long long using namespace std; int n,m,c,k; int ans,dw,ls; signed main() { cin>>n>>m>>c>>k; for(int i=1;i<=n;i++) { int x; cin>>x; dw|=x; } for(int i=1;i<=m;i++) { int p,q; cin>>p>>q; ls|=1ull<<p; } for(int i=0;i<k;i++) if(!((ls>>i)&1)||((dw>>i)&1)) ans++; if(ans==64&&n==0) cout<<"18446744073709551616"<<'\n';//特判 else { if(ans==64) cout<<-n<<'\n'; else cout<<(1ull<<ans)-n<<'\n'; } return 0; }
T3 函式呼叫
思路
當時考場上以為很簡單,打了這個,結果空間爆了
所以我們進行一波看
我們可以先處理乘法,然後對於每次加法再弄一個乘法操作,相當於打一個懶惰標記
然後我們再次瞭解發現,函式的呼叫關係實際上就是一個拓撲序
我們做兩次拓撲排序,一次處理函式執行一次後乘的倍數
另一次處理函式等價於執行多少次
因為乘法執行一次,等於加法執行若干次
這樣就做出來了
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> #include <queue> using namespace std; const int maxn=100005; const int mod=998244353; int n,m,q; int cnt[maxn]; int a[maxn]; int type[maxn],mul[maxn],add[maxn],pos[maxn]; int out1[maxn],out2[maxn]; vector<int> g1[maxn],g2[maxn]; void topo1() { queue <int> q; for(int i=0;i<=m;i++) { out1[i]=g2[i].size(); if(out1[i]==0) q.push(i); } while(q.size()) { int x=q.front(); q.pop(); for(int i=0;i<g1[x].size();i++) { int e=g1[x][i]; mul[e]=1ll*mul[e]*mul[x]%mod; out1[e]--; if(out1[e]==0)q.push(e); } } } void topo2() { queue <int> q; for(int i=0;i<=m;i++) { out2[i]=g1[i].size(); if(out2[i]==0) q.push(i); } while(q.size()) { int x=q.front(); int tag=1; q.pop(); for(int i=g2[x].size();i>0;--i) { int e=g2[x][i-1]; cnt[e]=(cnt[e]+1ll*cnt[x]*tag)%mod; tag=1ll*tag*mul[e]%mod; out2[e]--; if(out2[e]==0) q.push(e); } } } int main() { std::ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; cin>>m; mul[0]=1; for(int i=1;i<=m;i++) { cin>>type[i]; if(type[i]==1) { mul[i]=1; cin>>pos[i]>>add[i]; } if(type[i]==2) cin>>mul[i]; if(type[i]==3) { mul[i]=1; int c; cin>>c; for(int j=0;j<c;++j) { int e; cin>>e; g1[e].push_back(i); g2[i].push_back(e); } } } cin>>q; cnt[0]=1; while(q--) { int x; cin>>x; g1[x].push_back(0); g2[0].push_back(x); } topo1(); topo2(); for(int i=1;i<=n;i++) a[i]=1ll*a[i]*mul[0]%mod; for(int i=1;i<=m;i++) { if(type[i]==1) a[pos[i]]=(a[pos[i]]+1ll*cnt[i]*add[i])%mod; } for(int i=1;i<=n;i++) cout<<a[i]<<" "; return 0; }
T4 貪吃蛇
思路
貪心
有一個結論,如果當前最強的蛇吃掉的最蒻的蛇之後,沒有變成最蒻的蛇,那麼他一定會繼續吃
現在考慮最強的蛇,如果吃掉一條蛇之後,他還是最強的蛇的話,肯定會吃
如果吃掉一條蛇之後最強的蛇不是他了,這個時候,新的最強的蛇沒有剛才強,現在最蒻的蛇也沒有剛才蒻,第二強的蛇吃掉了最蒻的蛇之後一定沒有第一次最強的更強,所以會死在第一次最強的前面,所以如果這個蛇能想辦法不死的話,那麼第一次最強的蛇也一定不會死
如果吃完蛇之後會變成最蒻的蛇,吃不吃呢?
如果吃的話,我們第二次最強的蛇如果吃掉了第一次最強的蛇
如果不是最蒻的蛇的話,那麼第二次最強的蛇一定會吃掉第一次最強的蛇,那麼第一次最強的蛇則不會選擇吃最蒻的蛇
同理,如果第二次最強的蛇吃掉最蒻的蛇之後變成了最蒻的蛇,我們按照剛才相同的思路考慮第三條蛇吃不吃,依次考慮下去,子子孫孫無窮匱也
所以我們只用模擬一下兩個階段
第一個階段是,所有最強的蛇進食之後肯定不是最強的蛇,所以我們直接吃最蒻的蛇
第二個階段是,所有最強的蛇進食之後都是最蒻的蛇,直到有一條蛇可以放心大膽吃
階段一結束的時候,遊戲就基本完了
我們可以用雙端佇列來維護蛇
因為用雙端佇列是有序進來的,所以具有單調性
對於第一個階段和第二個階段,我們這麼模擬
第一個階段:
我們每次從 \(q_1,q_2\) 的尾部取出最強的蛇,從 \(q_1\) 的頭部取出最蒻的蛇,如果吃完了之後是最蒻的,那就進入第二階段,否則裝進 \(q_2\) 的頭部,繼續上述過程
第二個階段:
此時最蒻的蛇沒必要在入隊了,我們一直進食,知道總共的蛇的數量等於 \(2\) 或者進食之後不是最蒻的為止,我們找最強的蛇依然是從 \(q_1,q_2\) 的隊尾找
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
const int maxn=1000005;
const int inf=1e9;
int T,n,a[maxn];
pair <int,int> q1[maxn],q2[maxn];
int l1,r1,l2,r2;
pair <int,int> get_max()//取最大蛇
{
if(r1==l1)
return q2[l2++];
else if(r2==l2)
return q1[--r1];
else if(q2[l2]>q1[r1-1])
return q2[l2++];
else return q1[--r1];
}
pair <int,int> get_min()//取最小
{
if(l1==r1)
return q2[--r2];
else if(r2==l2)
return q1[l1++];
else if(q2[r2-1]<q1[l1])
return q2[--r2];
else return q1[l1++];
}
pair <int,int> Min(pair <int,int> x,pair <int,int> y)
{
return x<y?x:y;
}
inline void solve()
{
l1=r1=l2=r2=0;
for(int i=1;i<=n;++i)
q1[r1++]=make_pair(a[i],i);
int flag=0,cnt=0,alf=0;
while(1)
{
cnt++;
pair <int,int> x=get_min(),y=get_max();
pair <int,int> z=Min((l1<r1?q1[l1]:make_pair(inf,-inf)),(l2<r2?q2[r2-1]:make_pair(inf,-inf)));
y.first-=x.first;
if(y>z||cnt==n-1)
{
if(flag)
{
cout<<n-(flag-(alf&1))<<'\n';
return ;
}
if(cnt==n-1)
{
cout<<1<<'\n';
return ;
}
q2[r2++]=y;
}
else
{
alf++;
if(!flag)
flag=cnt;
q2[r2++]=y;
}
}
}
int main()
{
cin>>T;
T--;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
solve();
while(T--)
{
int k;
cin>>k;
for(int i=1;i<=k;i++)
{
int x;
cin>>x;
cin>>a[x];
}
solve();
}
return 0;
}
本博文為wweiyi原創,若想轉載請聯絡作者,qq:2844938982