[計蒜客NOIP模擬賽]2017.7.28Day1回顧反思總結
阿新 • • 發佈:2017-07-30
n) 模擬 查詢 同時 結點 reg etc mes 停止
D1T1 打地鼠
題目鏈接
反思-
比賽得分-0
思考:
比賽時,以為T1是一道常規模擬題目,沒怎麽看數據範圍。直接手動模擬,模擬完之後太自信也沒有造數據Hack自己的程序。直接導致爆0。同時發現自己對二維前綴和的學習也只是在皮毛之上,沒有深入思考與理解。
解題思路-
將圖像旋轉45°之後用二維前綴和維護,每次O(1)查詢,時間復雜度O(N*N)。
但是目前覺得這個圖像旋轉45°難以理解,打算手動模擬加深理解。
標程
#include<bits/stdc++.h> using namespace std; int n,m,k; inta[4050][4050],b[4050][4050],s[4050][4050],vis[4050][4050]; int main(){ //freopen("mouse10.in","r",stdin); //freopen("mouse10.ans","w",stdout); scanf("%d%d%d",&n,&k);m=n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); int l=n+n-1,ll=2*l-1; for(int i=1;i<=ll;i++){ if(i>=n&&(i-n)%2==0){ int num=(i-n)/2+1; //if(i==9) cout<<num<<endl; for(int j=num;j<=num+n-1;j++) b[j][i+1-j]=a[j-num+1][num],vis[j][i+1-j]=1; } } for(int i=1;i<=l;i++)for(int j=1;j<=l;j++) s[i][j]=b[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1]; int ans=0; for(int i=1;i<=l;i++) for(int j=1;j<=l;j++) if(vis[i][j]){ int zsx=max(1,i-k+1),zsy=max(1,j-k+1),zxx=min(l,i+k-1),zxy=max(1,j-k+1); int ysx=max(1,i-k+1),ysy=min(l,j+k-1),yxx=min(l,i+k-1),yxy=min(l,j+k-1); ans=max(ans,s[yxx][yxy]-s[ysx-1][yxy]-s[zxx][zxy-1]+s[zsx-1][zsy-1]); } printf("%d\n",ans); }
D1T2 蒜頭君的樹
反思-
比賽得分-30
思考:
比賽時,以為是用數據結構維護,生硬套上了樹剖模板,但是得分只有30。沒有深入去思考,每個變化的邊的所帶來的影響。
解題思路-
乘法原理在樹上的運用,我們假設結點u是結點v的父親,其邊權為w。那麽這條邊權會被最終答案計算多少次呢?沒錯就是除兒子v及其子結點的數量*兒子v及其子結點的數量。
修改的話只考慮,修改的那條邊對其他結點的影響。 我們假設修改的結點是 x,那麽影響的結點數量就是x及其子孫數量*其他結點數。
#include <cstdio> #include <algorithm> #include <vector> typedef long long ll; const int maxn = 1e5+5; struct node{ int v,w; }; std::vector<node>G[maxn]; int n,m,bian[maxn]; ll sum[maxn],dp[maxn]; int Read(){ int ch = 0; char c; c = getchar(); while( c<‘0‘ || c>‘9‘) c = getchar(); while( c>=‘0‘ && c<=‘9‘) { ch = ch*10+c-‘0‘; c = getchar(); } return ch; } void dfs(int x,int fa){ sum[x] = 1; int Size = G[x].size(); for(int i=0;i<Size;i++){ int v = G[x][i].v; int w = G[x][i].w; if(v==fa) continue; dfs(v,x); sum[x]+=sum[v]; dp[x]+=(dp[v]+((n-sum[v])*sum[v])*w); //關鍵部分 } } void AddEdge(int u,int v,int w){ node now; now.v=v; now.w=w; G[u].push_back(now); now.v=u; G[v].push_back(now); } int main(){ n = Read(); for(register int i=1;i<n;i++){ int v,w; v = Read(); w = Read(); AddEdge(i+1,v,w); bian[i+1]=w; } dfs(1,0); printf("%lld\n",dp[1]); m = Read(); for(int i=1;i<=m;i++){ int x,y; x = Read(); y = Read(); ll ans = dp[1]+sum[x]*(n-sum[x])*(y-bian[x]);//只考慮修改的邊影響的結果 bian[x] = y; dp[1] = ans; printf("%lld\n",ans); } return 0; }
D1T3 蒜頭君的坐騎
題目鏈接
反思-
比賽得分-0
思考:
比賽時,在T2耗費了兩小時,這個時間沒有時間思考了。所以草草寫了一個我自己都看不懂的辣雞暴力,成功爆0!
解題思路-
暴力搜索30分
用dp[n][m][k]表示牛神現在在(n,m)點,已經使用了k次技能.對於每次決策,如果不使用技能,可以轉移到dp[n][m+1][k],dp[n+1][m][k],如果使用技能,則可通過dfs搜索可以轉移到的每一個點,時間復雜度O(n*m*k).
不得不說這是第一次見到用DFS來求DP值,算是大開眼界了。反反復復閱讀與抄寫了四遍之後。看懂了,DFS和DP的含義。
#include<bits/stdc++.h> using namespace std; int n,m,t,k,h,atk; int dp[1050][1050][15],mp[1050][1050]; int cal(int x,int y,int at){ //計算怪物對你造成的傷害 h-1的原因是如果h==at那麽應該是秒殺不造成傷害 return (h-1)/at*mp[x][y]; } void dfs(int x,int y,int res,int st,int at,int dam){ if(st) dam+=cal(x,y,at); //在使用技能時,應計算是否受到傷害 //更新最終停止在x,y點的使用技能情況下的最小傷害值 太妙了 if(st==k) {dp[x][y][res+1]=min(dp[x][y][res+1],dam);return;} if(x+1<=n) dfs(x+1,y,res,st+1,at+mp[x+1][y],dam);//往下走的情況 if(y+1<=m) dfs(x,y+1,res,st+1,at+mp[x][y+1],dam);//往右走的情況 } int main(){ scanf("%d%d%d%d%d%d",&n,&m,&t,&k,&h,&atk); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int l=0;l<=t;l++) dp[i][j][l]=999999999; //在起點沒有使用過技能所以為0 dp[1][1][0]=0; for(int l=0;l<=t;l++) for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ //先考慮如果不使用技能,往下走的情況的最小傷害 if(i+1<=n) dp[i+1][j][l]=min(dp[i+1][j][l],dp[i][j][l]+cal(i+1,j,atk)); //不使用技能,往右走的最小傷害 if(j+1<=m) dp[i][j+1][l]=min(dp[i][j+1][l],dp[i][j][l]+cal(i,j+1,atk)); //如果技能還沒使用完 那麽就考慮一下在此點使用技能之後的情況 if(l!=t) dfs(i,j,l,0,atk,dp[i][j][l]); } cout<<dp[n][m][t]<<endl; }
最終總結反思
技不如人就是事實,沒有天賦還天天頹。再這樣下去,NOIP一等都是一個問題,所以拼命努力吧~
還有就是考試技巧非常重要,多參加比賽練習賽感,減少比賽失誤!!!
[計蒜客NOIP模擬賽]2017.7.28Day1回顧反思總結