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(" ");*/ //printf("l = %d r = %d ", l, r); if (r <= s && r != -1 && l != -1) { //printf("l = %d r = %d ",l,r); for (i = 0; i < m; i++) { if (d[i] <= l && d[i + 1] > l) { break; } } ans += s - d[i]; //printf("ans = %d ",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 ",ans); ans += 2 * (s - d[i]); //printf(" %d %d %d %d %d ", 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 ", ans,d[i + 1]); } printf("%d ", 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 ",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 ",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 ",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 ",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 ",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 ",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 ",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了,哭了)。