T1 方程
描述
給出非負整數 \(N\) ,統計不定方程 \(X+Y^2+Z^3=N\) 的非負整數解 \((X,Y,Z)\) 的數量。
輸入
輸入資料,包含一個非負整數 \(N\)。
輸出
輸出資料,包含一個非負整數表示解的數量。
資料範圍
40%的資料,\(N<=10000\)
60%的資料,\(N<=10^8\)
100%的資料,\(N<=10^{16}\)
分析
看到這個資料範圍,應該只能從 \(Z\) 開始列舉。
移項後可得 \(X+Y^2=N-Z^3\) ,所以 \(0\leq Y\leq\sqrt{N-Z^3}\)。
注意,我們對於這個 \(Y\) 的具體值不感興趣,我們只關心有多少個,而此時有 \((\sqrt{N-Z^3}+1)\) 個合法的 \(Y\)。此時, \(X=N-Z^3-Y^2\) ,顯然也是一個非負整數。
所以,我們只需要從小到大列舉 \(Z\),將 \((\sqrt{N-Z^3}+1)\) 新增到 \(ans\) 即可。
時間複雜度 \(O(\sqrt[3]{n})\)(sqrt視作常數複雜度)。
程式碼
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll n,ans;
int main(){
scanf("%lld",&n);
for(ll z=0;z*z*z<=n;z++)
ans+=ll(sqrt(n-z*z*z))+1ll;
printf("%lld",ans);
return 0;
}
T2 打磚塊
描述
在一個凹槽中放置了 \(n\) 層磚塊,最上面的一層有 \(n\) 塊磚,從上到下每層依次減少一塊磚。每塊磚都有一個分值,敲掉這塊磚就能得到相應的分值,如下圖所示。
如果你要敲掉第 \(i\) 層的第 \(j\) 塊磚的話,若 \(i=1\),你可以直接敲掉它;若 \(i>1\),則你必須先敲掉第 \((i-1)\) 層的第 \(j\) 和第 \(j+1\) 塊磚。
你現在可以敲掉最多m塊磚,求得分最多能有多少。
輸入
輸入的第一行為兩個正整數 \(n,m\);接下來 \(n\) 行,描述這 \(n\) 層磚塊上的分值 \(a[i][j]\),滿足 \(0\leq a[i][j] \leq 100\)。
輸出
輸出僅一行為一個正整數,表示被敲掉磚塊的最大價值總和。
資料範圍
對於 \(20\%\) 的資料,滿足\(1\leq n\leq 10, 1\leq m\leq 30\);
對於 \(100\%\) 的資料,滿足\(1\leq n\leq 50, 1\leq m\leq 500\)。
分析
一眼DP。
不過,看圖之後發現,這個圖並不存在決策單調性的性質。
不過旋轉之後,就可以變成前 \(i\) 行與前 \(j\) 列的dp了。
這裡透過一個輸入就能完成旋轉:
for(int j=1;j<=n;j++){
for(int i=n;i>=j;i--){
scanf("%d",&brick[i][j]);
}
}
設 \(dp[i][j][k]\) 代表前 \(i\) 行中,第 \(i\) 行選了 \(j\) 個、一共選了 \(k\) 個的最大值,然後進行dp即可。
還有就是記得提前算每行的字首和\(sum[i][j]\),最佳化速度。
PS:這道題的限制條件挺多的...所以寫出了諸如 \(mxg, mxj, mxk\) 之類的抽象玩意。
\(g\) 是總共選擇了多少,不能超過 \(\sum\limits_{a=1}^i a\) 的同時也不能超過 \(m\)。
\(j\) 不能超過 \(i\)(不能無中生有是吧),也不能超過 \(g\)。
\(k\) 同理,不能超過 \(i-1\) 的同時也不能超過 \(g-j\)。
最壞時間複雜度 \(O(nm^3)\),實際上遠遠跑不到。
程式碼
#include<bits/stdc++.h>
#define N 55
using namespace std;
int n,m,brick[N][N],dp[N][N][2005],sum[N][N];
int ans;
int main(){
scanf("%d%d",&n,&m);
for(int j=1;j<=n;j++){
for(int i=n;i>=j;i--){
scanf("%d",&brick[i][j]);
}
}
/*
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cout<<brick[i][j]<<' ';
}
cout<<endl;
}*/
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
sum[i][j]=sum[i][j-1]+brick[i][j];
}
}
for(int i=1;i<=n;i++){
int mxg=min((i*(i+1))>>1,m),mxj,mxk;
for(int g=0;g<=mxg;g++){
mxj=min(i,g);
for(int j=0;j<=mxj;j++){
mxk=min(g-j,i-1);
for(int k=max(j-1,0);k<=mxk;k++){
if(dp[i-1][k][g-j]+sum[i][j]>dp[i][j][g])
dp[i][j][g]=dp[i-1][k][g-j]+sum[i][j];
}
}
}
}
for(int j=0;j<=n;j++){
if(dp[n][j][m]>ans)
ans=dp[n][j][m];
}
printf("%d",ans);
return 0;
}
T3 打磚塊
描述
有兩個長度為 \(N\) 的序列 \(A\) 和 \(B\),在 \(A\) 和 \(B\) 中各任取一個數相加可以得到 \(N^2\) 個和,求這 \(N^2\) 個和中最小的 \(N\) 個。
輸入
第一行輸入一個正整數 \(N\) ;第二行 \(N\) 個整數 \(A_i\) 且 \(A_i\leq 10^9\);第三行 \(N\) 個整數 \(B_i\) 且\(B_i\leq 10^9\)。
輸出
輸出僅一行,包含 \(N\) 個整數,從小到大輸出這 \(N\) 個最小的和,相鄰數字之間用空格隔開。
資料範圍
對於 \(40\%\)的資料,滿足\(1\leq N \leq 500\);
對於 \(100\%\)的資料,滿足\(1\leq N \leq 100000\);
分析
如果這道題只要\(40\)分的話,其實二重迴圈列舉 \(A_i+B_i\) 即可。時間複雜度 \(O(n^2)\)。
如果你想要AC的話...
我們不妨建立一個優先佇列,開始時將 \(A_i+B_1\) 放入優先佇列裡。如果此時的最小值為 \(A_x+B_1\),則取出 \(A_x+B_1\)並記錄,同時將 \(A_x+B_2\) 放入優先佇列中。對於整個過程,也是同理:取出優先佇列的最小值 \(A_x+B_y\)並記錄,再將 \(A_x+B_{y+1}\) 放入優先佇列中。重複 \(N\) 遍。時間複雜度 \(O(n\log n)\)。
程式碼
#include<bits/stdc++.h>
#define N 100005
using namespace std;
using PII=pair<int,int>;
bool cmp(PII a,PII b){
return a.first<b.first;
}
priority_queue<PII,vector<PII>,greater<PII> > q;
int n,a[N],b[N],cnt[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
cnt[i]=1;
scanf("%d",a+i);
}
for(int i=1;i<=n;i++){
scanf("%d",b+i);
}
sort(a+1,a+n+1);
sort(b+1,b+n+1);
for(int i=1;i<=n;i++){
q.push(make_pair(a[i]+b[1],i));
}
for(int i=1;i<=n;i++){
PII tmp=q.top();
q.pop();
printf("%d ",tmp.first);
q.push(make_pair(a[tmp.second]+b[++cnt[tmp.second]],tmp.second));
}
return 0;
}
T4 最小密度路徑
描述
給出了一張有 \(N\) 個點 \(M\) 條邊的加權有向無環圖,接下來有 \(Q\) 個詢問,每個詢問包括 \(2\) 個節點 \(X\) 和 \(Y\),要求算出從 \(X\) 到 \(Y\) 的一條路徑,使得密度最小(密度的定義為,路徑上邊的權值和除以邊的數量)。
輸入
第 $1 行包括2個整數 \(N\) 和 \(M\)。
第 $2 $ 到 \(M+1\) 行,每行三個數字 \(A,B,W\) ,表示從 \(A\) 到 \(B\) 有一條權值為 \(W\) 的有向邊。
第 \(M+2\) 行只有一個整數 \(Q\)。
接下來 \(Q\) 行,每行有兩個正整數 \(X, Y\),表示一個詢問。
輸出
對於每個詢問輸出一行,表示該詢問的最小密度路徑的密度(保留 \(3\) 位小數)。
如果不存在從 \(X\) 到 \(Y\) 的一條路徑,則輸出"OMG!"(不含引號)。
資料範圍
對於 \(50\%\) 的資料,有 \(1\leq N\leq 10, 1\leq M\leq 100, 1\leq W\leq 1000, 1\leq Q\leq 1000\);
對於 \(100\%\) 的資料,有 \(1\leq N\leq 50, 1\leq M\leq 1000, 1\leq W\leq 100000, 1\leq Q\leq 100000\)。
分析
考場上時間不夠了。(主要是T2時間想的太久了)
所以寫了個dfs爆搜。(喜得54分)
程式碼
#include<bits/stdc++.h>
#define N 100005
#define M 55
using namespace std;
int n,m,Q;
const double INF=1145141919810.0;
struct EDGE{
int to,nxt,v;
}e[N];
int head[N],cnt=0;
double dis[M][M];
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].v=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void dfs(int x,double sum,int edgeCnt,int origin)
{
if(x!=origin)
dis[origin][x]=min(dis[origin][x],sum/double(edgeCnt));
for(int i=head[x];i;i=e[i].nxt){
dfs(e[i].to,sum+double(e[i].v),edgeCnt+1,origin);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dis[i][j]=INF;
}
dis[i][i]=0;
}
for(int i=1;i<=n;i++)
dfs(i,0,0,i);
scanf("%d",&Q);
while(Q--){
int x,y;
scanf("%d%d",&x,&y);
if(dis[x][y]==INF)
printf("OMG!\n");
else
printf("%.3lf\n",dis[x][y]);
}
return 0;
}