NOIP2018普及組題解
雖然已經是TG選手了,但是作為一個初中生還是要做做PJ噠>_<.
T1:
題目:luogu5015.
題目大意:將輸入的字元中的小寫字母、大寫字母和數字的總數量輸出.
水題,用getchar水過就可以了.
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void typedef long long LL; int sum; Abigail into(){ char c=getchar(); while (c!=EOF){ if (c<='Z'&&c>='A'||c<='z'&&c>='a'||c<='9'&&c>='0') ++sum; c=getchar(); } } Abigail outo(){ printf("%d\n",sum); } int main(){ into(); outo(); return 0; }
T2:
題目:luogu5016.
題目大意:給定一個數組A,現在在第m個點前的都屬於龍,後面的都屬於虎,第m個不屬於任意一方.現在給Ai加了s1,要求再給一個Aj加s2,使得兩方氣勢差最小.其中一方的氣勢定義為.
其實這道題就是算出兩方的氣勢,然後暴力列舉加入的位置就可以了.
不懂為什麼一群人說被long long坑了,這不是明顯要開long long嗎...
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void typedef long long LL; const int N=100000; const LL INF=(1LL<<63)-1LL; //寫成1LL<<60的我被卡精度了 LL a[N+9],sum1,sum2,a1,a2,ans; int n,m,p,v; Abigail into(){ scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%lld",&a[i]); scanf("%d%d%lld%lld",&m,&p,&a1,&a2); } Abigail work(){ a[p]+=a1; for (int i=1;i<m;++i) sum1+=a[i]*(m-i); for (int i=m+1;i<=n;++i) sum2+=a[i]*(i-m); ans=INF;v=1; for (int i=1;i<m;++i) if (ans>abs(sum1+a2*(m-i)-sum2)) ans=abs(sum1+a2*(m-i)-sum2),v=i; if (ans>abs(sum1-sum2)) ans=abs(sum1-sum2),v=m; for (int i=m+1;i<=n;++i) if (ans>abs(sum1-sum2-a2*(i-m))) ans=abs(sum1-sum2-a2*(i-m)),v=i; } Abigail outo(){ printf("%d\n",v); } int main(){ into(); work(); outo(); return 0; }
T3:
題目:luogu5017.
題目大意:給定一輛車往返一趟的時間m,並且車的容量無限大.現在要送n個人,人i會在時刻ti到達車站,問車這n個人等車時間之和最小是多少.
據說可以以時間為狀態,然後斜率優化一下可以做到,但是我不會.
我們可以設f[i]表示送前i個同學需要的最少等待時間,然後直接列舉從前面那個狀態轉移過來,可以做到.
但是很明顯這個演算法是不滿足最優子結構的,因為總等待時間最短不一定代表回到起點的時間點最早.
我們考慮先預處理兩個陣列c[i]和s[i],分別表示到時刻i時的人的數量與乘客到達時間的字首和.
那麼我們考慮設f[i]表示時刻i發一輛車時的最少總等待時間,然後列舉上一次的發車時刻j來轉移,時間複雜度
對於這個演算法,我們可以列出方程:
考慮一個狀態f[i]從f[j]轉移過來,可以發現若,明顯可以讓這兩次發車之間多發一次車來使答案不會變得更劣,那麼我們就可以將列舉j變成列舉時差來優化上面的演算法,時間複雜度.
那麼方程就變為:
繼續考慮優化這個演算法,發現一輛車的發車時間只會是,其中.那麼我們可以只計算有用的狀態,不去計算無用的狀態,也就是說我們列舉到一個狀態f[i]時,判斷這個狀態是否有一個t[j],使得,具體可以通過是否為0來判斷.這樣我們就可以做到.
然後貌似還可以把這個演算法優化一下,類似於斜率優化,可以做到,但是我不會.
演算法程式碼如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=500,M=100,T=5*1000000;
const int INF=(1<<29)-1;
int n,m,t[N+9];
int c[T+9],s[T+9],mt;
//int f[N+9][M*2+9];
int f[T+9];
Abigail into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i){
scanf("%d",&t[i]);
mt=max(mt,t[i]);
++c[t[i]];s[t[i]]+=t[i];
}
}
Abigail work(){
mt+=m-1;
sort(t+1,t+1+n);
for (int i=1;i<=mt;++i)
c[i]+=c[i-1],s[i]+=s[i-1];
for (int i=0;i<=mt;++i)
f[i]=i*c[i]-s[i];
for (int i=0;i<=mt;++i){
if (!(c[i]-c[i-m])&&i>m){
f[i]=f[i-m]; //避免一些麻煩的處理
continue;
}
f[i]=i*c[i]-s[i];
for (int j=max(0,i-(m<<1));j<=i-m;++j)
f[i]=min(f[i],f[j]+i*(c[i]-c[j])-s[i]+s[j]);
}
}
Abigail outo(){
int ans=INF;
for (int i=t[n];i<=mt;++i)
ans=min(ans,f[i]);
printf("%d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}
T4:
題目:luogu5018.
題目大意:給定一棵二叉樹,要求一棵最大的子樹,使得這棵子樹翻轉過後與原子樹一模一樣(點權、結構、大小等).
感覺很難的一道題,也沒做過類似的題,看到題面嚇懵了,PJ考的都是什麼毒瘤題啊.
事實上,我們只要考慮以每個點為根,暴力判斷這棵子樹是否對稱,並在判定的時候及時彈出.這樣看起來是的,事實上是的.
為什麼呢?首先我們在遍歷的時候,每次判定,我們只要一旦發現就夠不同就會彈出,那麼很顯然最壞的情況肯定是當一棵樹是完全二叉樹的時候.而完全二叉樹的深度是的,我們在每一層都掃了一遍節點,所以時間複雜度為.
具體各種剪枝看程式碼:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=1000000;
int n,v[N+9],ls[N+9],rs[N+9],siz[N+9],ans;
void dfs(int k){
siz[k]=1;
if (ls[k]) dfs(ls[k]);
if (rs[k]) dfs(rs[k]);
siz[k]+=siz[ls[k]]+siz[rs[k]];
}
bool check(int r1,int r2){
if (r1+r2==0) return true; //若r1=r2=0,則匹配
if (v[r1]^v[r2]) return false; //兩邊點權是否相同
if ((siz[ls[r1]]^siz[rs[r2]])||(siz[rs[r1]]^siz[ls[r2]])) return false; //兩邊是否大小/結構對稱
if (!check(ls[r1],rs[r2])) return false; //若r1的左兒子與r2的右兒子不匹配
if (!check(rs[r1],ls[r2])) return false; //若r1的右兒子與r2的左兒子不匹配
return true;
}
Abigail into(){
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&v[i]);
for (int i=1;i<=n;++i){
scanf("%d%d",&ls[i],&rs[i]);
if (ls[i]==-1) ls[i]=0;
if (rs[i]==-1) rs[i]=0;
}
}
Abigail work(){
dfs(1);
for (int i=1;i<=n;++i)
if (check(i,i)) ans=max(ans,siz[i]);
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}