solve:4/11
補題:6/11
補題:zz
這是一道分類討論的題目,有一個規律就是如果必須要從第一個區到第二個區,那么最多轉區兩次(1到2一次,2到1一次),然后分類討論即可,只要細心一定能做出來。
//#pragma comment(linker, "/STACK:102400000,102400000") #include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<string> #include<math.h> #include<cmath> #include<time.h> #include<map> #include<set> #include<vector> #include<queue> #include<algorithm> #include<numeric> #include<stack> #include<bitset> #include<unordered_map> const int maxn = 0x3f3f3f3f; const double EI = 2.71828182845904523536028747135266249775724709369995957496696762772407663035354594571382178525166427; const double PI = 3.141592653589793238462643383279; //#ifdef TRUETRUE //#define gets gets_s //#endif using namespace std; int c[2][1000010]; int z[2][100010], cnt[5]; int d[111]; int main(void) { //ios::sync_with_stdio(false); int n, rr, k, m, s, i, x, y, l1, l2, r1, r2, ans, l, r, j; while (~scanf("%d %d %d %d %d", &n, &rr, &m, &k, &s)) { memset(c, 0, sizeof(c)); cnt[0] = 0; cnt[1] = 0; for (i = 0; i < rr; i++) { scanf("%d %d", &x, &y); if (x == s && y == 0) { continue; } c[y][x] = 1; z[y][cnt[y]++] = x; } for (i = 0; i < m; i++) { scanf("%d", d + i); } sort(z[0], z[0] + cnt[0]); sort(z[1], z[1] + cnt[1]); l1 = -1; l2 = -1; r1 = -1; r2 = -1; if (cnt[0]) { l1 = z[0][0]; r1 = z[0][cnt[0] - 1]; } if (cnt[1]) { l2 = z[1][0]; r2 = z[1][cnt[1] - 1]; } ans = 0; if (l2 != -1) { ans += k * 2; } l = min(l1, l2); if (l1 == -1) { l = l2; } else if (l2 == -1) { l = l1; } r = max(r1, r2); d[m++] = 1; d[m++] = n; d[m++] = n + 1; sort(d, d + m); /*for (i = 0;i < m;i++) { printf("%d ", d[i]); } printf("\n");*/ //printf("l = %d r = %d\n", l, r); if (r <= s && r != -1 && l != -1) { //printf("l = %d r = %d\n",l,r); for (i = 0; i < m; i++) { if (d[i] <= l && d[i + 1] > l) { break; } } ans += s - d[i]; //printf("ans = %d\n",ans); if (l2 != -1) { for (j = 0; j < m; j++) { if (d[j] >= r2) { break; } } ans += d[j] - d[i]; ans += abs(s - d[j]); } else { ans += s - d[i]; } } else if (l >= s && r != -1 && l != -1) { for (i = 0; i < m; i++) { if (d[i] >= r) { break; } } ans += d[i] - s; if (l2 != -1) { for (j = m - 1; j >= 0; j--) { if (d[j] <= l2) { break; } } ans += d[i] - d[j]; ans += abs(s - d[j]); } else { ans += d[i] - s; } } else if (l <= s && r >= s && r != -1 && l != -1) { for (i = 0; i < m; i++) { if (d[i] <= l && d[i + 1] > l) { break; } } //printf(" %d\n",ans); ans += 2 * (s - d[i]); //printf(" %d %d %d %d %d\n", ans,d[i],i,l,r); for (i = 0; i < m; i++) { if (d[i] <= r && d[i + 1] >= r) { break; } } ans += 2 * (d[i + 1] - s); //printf(" %d %d\n", ans,d[i + 1]); } printf("%d\n", ans); } return 0; }
Code:pai爺
Thinking: pai爺 KK
dp[ i ][ j ][ k ]表示 i 行 j 列 在第 k 秒的時候吃到的最多的豆豆數量,從五個方向轉移(上下左右加自己)。k的范圍最大只有1018*10,然后用滾動數組滾動掉,就可以AC了。
補充:div1 1018變成了10^18,會有周期,尚未證明,k,k+t,k+2t。
#include<stdio.h> #include<algorithm> #include<cstring> #include<cstdio> #include<cmath> using namespace std; int ans=0,n,m,c,now=0,X1,X2,Y1,Y2,f[3][20][20],t[20][20]; void solve(int a,int b,int c,int d,int k) { if(t[a][b]!=0 && (k%t[a][b]==0)) f[!now][a][b]=max(f[now][c][d]+1,f[!now][a][b]); else f[!now][a][b]=max(f[now][c][d],f[!now][a][b]); } int main() { scanf("%d%d%d",&n,&m,&c); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) scanf("%d",&t[i][j]); scanf("%d%d%d%d",&X1,&Y1,&X2,&Y2); for(int k=0; k<=1; k++) for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) f[k][i][j]=-1; f[now][X1][Y1]=0; for(int k=1; k<=11000; k++) { for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) if(f[now][i][j]!=-1) { solve(i+1,j,i,j,k); solve(i-1,j,i,j,k); solve(i,j+1,i,j,k); solve(i,j-1,i,j,k); solve(i,j,i,j,k); if(f[!now][X2][Y2]>=c) ans=k; } if(ans) break; now=!now; } printf("%d\n",ans); }
unsolved
unsolved
補題:kk
樹形dp,比賽沒人做,以為是難題。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define CLR(a,b) memset(a,b,sizeof(a)); using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=10010; int head[maxn],tot,n; bool vis[maxn]; int f[maxn],d[maxn]; int dp[2][maxn]; struct edge{ int v,Next; }a[maxn<<2]; void init(){ tot=0,CLR(head,-1); CLR(vis,false); CLR(dp,0); } void addv(int u,int v) { a[++tot].v=v; a[tot].Next=head[u]; head[u]=tot; } void tdfs(int u,int fa){ int flag=0; vis[u]=1; dp[0][u]=0; dp[1][u]=f[u]; for(int i=head[u];i!=-1;i=a[i].Next) { int v=a[i].v; if(v==fa)continue; tdfs(v,u); flag=1; dp[0][u]+=max(dp[0][v],dp[1][v]); dp[1][u]+=max(dp[0][v],dp[1][v]-d[min(u,v)]); } } int main(){ cin>>n; init(); for(int i=1;i<=n;i++) { scanf("%d",&f[i]); } for(int i=1;i<=n;i++) { scanf("%d",&d[i]); } for(int i=2;i<=n;i++) { if(i%2==1) { addv(i,3*i+1); addv(3*i+1,i); } if(i%2==0){ addv(i,i/2); addv(i/2,i); } } int ans=0; for(int i=1;i<=n;i++) { if(vis[i]==0){ tdfs(i,0); ans+=max(dp[0][i],dp[1][i]); } } printf("%d\n",ans); }
Code:KK
Thinking:KK
對於一座山,如果要砍,就肯定要砍到(K+第一座山的高度),將這部分額外的花費加到邊的權值上就好了,雙向邊要分開建,權值不一定一樣,然后dijkstra跑一跑,水題。
1 #include<iostream> 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<string> 6 #include<math.h> 7 #include<cmath> 8 #include<time.h> 9 #include<map> 10 #include<set> 11 #include<vector> 12 #include<queue> 13 #include<algorithm> 14 #include<numeric> 15 #include<stack> 16 #include<bitset> 17 //#include<unordered_map> 18 const int inf = 0x3f3f3f3f; 19 const double EI = 2.71828182845904523536028747135266249775724709369995957496696762772407663035354594571382178525166427; 20 const double PI = 3.141592653589793238462643383279; 21 const int maxn=100010; 22 using namespace std; 23 typedef long long ll; 24 int n,m; 25 ll k; 26 int head[maxn],tot; 27 ll dis[maxn]; 28 29 struct edge{ 30 int v; 31 ll w; 32 int Next; 33 }a[maxn<<2]; 34 bool vis[maxn]; 35 ll h[maxn]; 36 void init(){ 37 tot=0,memset(head,-1,sizeof(head)); 38 } 39 void addv(int u,int v,ll w) 40 { 41 a[++tot].v=v; 42 a[tot].w=w; 43 a[tot].Next=head[u]; 44 head[u]=tot; 45 } 46 priority_queue< pair<ll,int>>q; 47 void dijkstra(){ 48 memset(vis,false,sizeof(vis)); 49 memset(dis,0x3f,sizeof(dis)); 50 dis[1]=0; 51 q.push(make_pair(0,1)); 52 while(!q.empty()){ 53 int x=q.top().second; 54 q.pop(); 55 if(vis[x])continue; 56 vis[x]=1; 57 for(int i=head[x];i!=-1;i=a[i].Next) 58 { 59 int y=a[i].v; 60 ll w=a[i].w; 61 if(dis[y]>dis[x]+w){ 62 dis[y]=dis[x]+w; 63 q.push(make_pair(-dis[y],y)); 64 } 65 } 66 } 67 } 68 int main(void) 69 { 70 //ios::sync_with_stdio(false); 71 cin>>n>>m>>k; 72 init(); 73 scanf("%lld",&h[1]); 74 k+=h[1]; 75 for(int i=2;i<=n;i++) 76 { 77 scanf("%lld",&h[i]); 78 } 79 int u,v; 80 ll w; 81 while(m--) 82 { 83 scanf("%d%d%lld",&u,&v,&w); 84 if(h[v]>=k) 85 addv(u,v,w+(h[v]-k)*(h[v]-k)); 86 else addv(u,v,w); 87 if(h[u]>=k) 88 addv(v,u,w+(h[u]-k)*(h[u]-k)); 89 else addv(v,u,w); 90 } 91 dijkstra(); 92 printf("%lld\n",dis[n]); 93 return 0; 94 }
unsolved
unsolved
Code:pai爺
Thinking:pai爺
div 2 樹狀數組+dp思想 O(n*n*log(n))

1 #include<stdio.h> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 using namespace std; 8 const int P=1e9+7; 9 int n,p[2010],w[2010],c[10100],q[2010][2010]; 10 int ans=0; 11 inline int lowbit(int x){return x&-x;} 12 void add(int x,int y) 13 { 14 for(;x<=n;x+=lowbit(x)) c[x]=(c[x]+y)%P; 15 } 16 int sum(int x) 17 { 18 int tmp=0; 19 for(;x;x-=lowbit(x)) tmp=(tmp+c[x])%P; 20 return tmp; 21 } 22 int main() 23 { 24 scanf("%d",&n); 25 for(int i=1;i<=n;i++) scanf("%d",&p[i]); 26 for(int i=1;i<=n;i++) 27 for(int j=i+1;j<=n;j++) 28 { 29 if(p[i]<p[j]) 30 { 31 q[i][0]++; 32 q[i][q[i][0]]=j; 33 //printf("%d %d\n",i,j); 34 } 35 } 36 for(int i=n;i>=1;i--) 37 { 38 w[i]=sum(p[i]-1); 39 ans=(ans+w[i])%P; 40 //printf("%d\n",ans); 41 w[i]++; 42 for(int j=1;j<=q[i][0];j++) add(p[q[i][j]],w[q[i][j]]); 43 } 44 printf("%d\n",ans); 45 }
Code:zz
Thinking:KK zz
div2:由於m最大只有一千,所以從(m+1)/2到1枚舉最后我需要買的物品,每次和當前答案更新。如一個人的物品比自己需要買的多或一樣多,那肯定先把多余這部分的全部買走,如果發現不夠,那么就在剩下的所有里面挑最便宜的;如果發現買完那些比自己大的,就已經超出當前的值了,說明不合法,就不更新答案。用multiset完成以上操作。
div1:m變成一萬,不能枚舉,想到二分,發現中間有一段區間雖然都是合法的,但是並不是買的物品越少錢越少(買的物品多了,強制買的物品就少,然后不足的就可以從其他物品里挑最便宜的),所以不滿足二分的性質,三分好像可以做(wls吐槽好像二字),標算是線段樹?還不會。
1 //#pragma comment(linker, "/STACK:102400000,102400000") 2 #include<iostream> 3 #include<stdio.h> 4 #include<stdlib.h> 5 #include<string.h> 6 #include<string> 7 #include<math.h> 8 #include<cmath> 9 #include<time.h> 10 #include<map> 11 #include<set> 12 #include<vector> 13 #include<queue> 14 #include<algorithm> 15 #include<numeric> 16 #include<stack> 17 #include<bitset> 18 //#include<unordered_map> 19 const int inf = 0x3f3f3f3f; 20 const int maxn=100010; 21 using namespace std; 22 typedef long long ll; 23 struct s 24 { 25 long long c[1010]; 26 int cnt; 27 }z[1010]; 28 multiset<long long>st; 29 multiset<long long>::iterator it; 30 inline bool comp(ll a,ll b) 31 { 32 return a > b; 33 } 34 int main(void) 35 { 36 //ios::sync_with_stdio(false); 37 int n,m,i,z2,j,num,x,k; 38 long long z1,sum,ans,ss; 39 while(~scanf("%d %d",&n,&m)) 40 { 41 st.clear(); 42 for(i = 1;i <= n;i++) 43 { 44 z[i].cnt = 0; 45 } 46 for(i = 0;i < m;i++) 47 { 48 scanf("%lld %d",&z1,&z2); 49 z[z2].c[z[z2].cnt++] = z1; 50 st.insert(z1); 51 } 52 for(i = 1;i <= n;i++) 53 { 54 sort(z[i].c,z[i].c + z[i].cnt,comp); 55 } 56 sum = 0; 57 num = 0; 58 ans = 10000000000000; 59 for(i = m / 2 + 1;i >= 1;i--) 60 { 61 for(j = 1;j <= n;j++) 62 { 63 if(z[j].cnt >= i) 64 { 65 while(z[j].cnt >= i) 66 { 67 num++; 68 sum += z[j].c[z[j].cnt - 1]; 69 st.erase(st.find(z[j].c[z[j].cnt - 1])); 70 z[j].cnt--; 71 } 72 } 73 } 74 if(num == i) 75 { 76 ans = min(sum,ans); 77 } 78 else if(num < i) 79 { 80 ss = sum; 81 x = i - num; 82 for(it = st.begin();it != st.end();it++) 83 { 84 sum += *it; 85 x--; 86 if(x == 0) 87 { 88 break; 89 } 90 } 91 ans = min(sum,ans); 92 sum = ss; 93 } 94 } 95 printf("%lld\n",ans); 96 } 97 return 0; 98 }
unsolved
建bfs樹
訓練總結:
KK: 簡單dp聽了pai爺的一些思路馬上意會到正解。最短路看錯樣例,看清后秒解,然后oj的排名炸了,以為今天只能做兩題,所以在夢游想題,后來發現J題數據量小,想了一下想到div2的正解,被zz優化了一下,剛好過了,自己寫可能會wa一發(因為沒有二分的性質,但是自己寫可能會找到一個答案就直接break),以后訓練要認真,很多題都是可以思考的,不到最后不要放棄。
pai爺:做題的時候缺少驗證吧,不夠認真對待題目,思路要清晰,想法很多,錯誤想法也很多。之后要大膽猜測以及大膽驗證。
zz:一開局看到了B,感覺和以前做的一道題很像,想到可能是一個三重循環的dp,但是時間那一維和狀態轉移方程沒想好,就沒說,看其他題去了;后來oj出了問題,基本上沒更新,題目基本上沒什么思路,過了很久以后,kk跟我說了J題的暴力做法,我看了看n和m才一千,感覺行,后來在寫的時候發現太暴力是要n^3的,就用multiset降到了n*n*log(n),剛好過(因為comp函數寫錯了wa了,哭了)。
