CCPC-Wannafly Winter Camp Day1 (Div2, onsite) A B C E F I J
A 机器人
链接:https://www.cometoj.com/contest/7/problem/A?problem_id=92
思路:
分两大类讨论:
1. B区没有点:
(1)点都在起点左边
(2)点都在起点右边
(3)起点两边都有点
2.B区有点:
(1)两个区的点都在起点左边
(2)两个区的点都在起点右边
(3)起点两边都有点
分类讨论下就好了,注意一个特殊情况当只经过一个点且这个点正好是起点的情况是输出0
实现代码;
#include<bits/stdc++.h> using namespace std; const int M = 1e6+10; vector<int>g[3]; const int inf = 0x3f3f3f3f; int p[M]; int main() { int n,r,m,k,s,x,y; cin>>n>>r>>m>>k>>s; for(int i = 1;i <= r;i ++){ cin>>x>>y; g[y].push_back(x); } for(int i = 0;i < m;i ++) cin>>p[i]; p[m] = 1; p[m+1] = n; m += 2; sort(g[0].begin(),g[0].end()); sort(g[1].begin(),g[1].end()); sort(p,p+m); int len1 = g[0].size(),len2 = g[1].size(); int l1,l2,r1,r2,ans=0; l1 = l2 = inf; r1 = r2 = 0; if(g[0].size()){ int a1 = lower_bound(p,p+m,g[0][len1-1])-p; int b1 = upper_bound(p,p+m,g[0][0])-p-1; l1 = p[b1]; r1 = p[a1]; } if(g[1].size()==0){ if(g[0].size()==1&&g[0][0]==s) ans = 0; else { if(g[0][0] >= s) ans = abs(r1-s)*2; else if(g[0][len1-1] <= s) ans = abs(l1-s)*2; else ans = 2*(abs(r1-s)+abs(l1-s)); } } else{ int a = lower_bound(p,p+m,g[1][len2-1])-p; int b = upper_bound(p,p+m,g[1][0])-p-1; l2 = p[b]; r2 = p[a]; int l = min(l1,l2),rr = max(r1,r2); if(l >= s) ans = 2*k + 2*abs(rr-s); else if(rr <= s) ans = 2*k + 2*abs(l-s); else ans = 2*k+2*(abs(l-s)+abs(rr-s)); } cout<<ans<<endl; }
B 吃豆豆
题目链接:https://www.cometoj.com/contest/7/problem/B?problem_id=93
思路;
构造一个三维数组dp[i][j][k] , 代表在格子i,j上第k秒最多有多少个糖果
维护起来就取前一个状态的五种可能性 dp[i+1][j][k-1] dp[i-1][j][k-1] dp[i][j+1][k-1] dp[i][j-1][k-1] dp[i][j][k-1] 中最大的就好了
实现代码;
#include<bits/stdc++.h> using namespace std; #define ll long long const int M = 2e4 +10; int dp[15][15][M]; int t[15][15]; int main() { int n,m,c,sx,sy,ex,ey; cin>>n>>m>>c; for(int i = 1;i <= n;i ++) for(int j = 1;j <= m;j ++) cin>>t[i][j]; cin>>sx>>sy>>ex>>ey; memset(dp,-M,sizeof(dp)); dp[sx][sy][0] = 0; for(int k = 1;k < M;k ++){ for(int i = 1;i <= n;i ++){ for(int j = 1;j <= m;j ++){ dp[i][j][k] = max(dp[i-1][j][k-1],max(dp[i+1][j][k-1],max(dp[i][j+1][k-1],max(dp[i][j-1][k-1],dp[i][j][k-1])))); if(k%t[i][j]==0) dp[i][j][k]++; } } } int ans = 0; for(int i = 0;i < M;i ++){ if(dp[ex][ey][i] >= c){ ans = i; break; } } cout<<ans<<endl; }
C 拆拆拆数
题目链接:https://www.cometoj.com/contest/7/problem/C?problem_id=94
思路:相邻的两个数互质,暴力找就好了
实现代码:
#include<bits/stdc++.h> using namespace std; #define ll long long int main() { ll a,b,n; cin>>n; for(ll i = 1;i <= n;i ++){ cin>>a>>b; if(__gcd(a,b)==1){ cout<<1<<endl; cout<<a<<" "<<b<<endl; } else { ll flag = 0; for(ll i = 2;i <= 100;i ++){ for(ll j = 2;j <= 100;j ++){ if(__gcd(a-i,b-j)==1&&__gcd(i,j)==1){ cout<<2<<endl; cout<<i<<" "<<j<<endl; cout<<a-i<<" "<<b-j<<endl; flag = 1; break; } } if(flag == 1) break; } } } return 0; }
E 流流流动
题目链接:https://www.cometoj.com/contest/7/problem/E?problem_id=106
思路:
树形dp,按照题目要求把数连起来,会形成一棵棵树,对于这个树按照题目要求我们可以得到以下状态转移方程:
dp[u][0] += max(dp[v][0],dp[v][1]);
dp[u][1] += max(dp[v][0],dp[v][1]-d[min(u,v)]);
dp[i][j] j==1代表取了u这个点,j==0代表没取
连边的话,直接用并查集就好了
实现代码:
#include<bits/stdc++.h> using namespace std; #define ll long long const int M = 1e3+10; struct node{ int next,to; }e[M]; ll dp[M][3],pre[M],f[M],d[M],cnt,head[M]; void add(ll u,ll v){ e[++cnt].to = v;e[cnt].next = head[u];head[u] = cnt; e[++cnt].to = u;e[cnt].next = head[v];head[v] = cnt; } int Find(int x){ if(x == pre[x]) return x; return pre[x] = Find(pre[x]); } void Union(int x,int y){ int fx = Find(x); int fy = Find(y); pre[fx] = fy; } void dfs(int u,int fa){ dp[u][1] = f[u];dp[u][0]=0; for(int i = head[u];i;i=e[i].next){ int v = e[i].to; if(v == fa) continue; dfs(v,u); dp[u][0] += max(dp[v][0],dp[v][1]); dp[u][1] += max(dp[v][0],dp[v][1]-d[min(u,v)]); } } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int n; cin>>n; for(int i = 1;i <= n;i ++) cin>>f[i],pre[i] = i; for(int i = 1;i <= n;i ++) cin>>d[i]; for(int i = 2;i <= n;i ++){ if((i%2==1)&&(i*3+1<=n)){ add(i*3+1,i); Union(i*3+1,i); } else if(i%2==0){ add(i/2,i); Union(i/2,i); } } ll ans = 0; for(int i = 1;i <= n;i ++){ if(pre[i]==i){ dfs(i,i); ans += max(dp[i][0],dp[i][1]); } } cout<<ans<<endl; }
F 爬爬爬山
题目链接:https://www.cometoj.com/contest/7/problem/F?problem_id=97
思路:基础最短路,只要加个判断就好了
实现代码:
#include<bits/stdc++.h> using namespace std; #define ll long long typedef pair<ll,ll> P; const int M = 2e5+10; const ll inf = 1e18; ll vis[M],dist[M],n,m,k,tot,a[M]; struct node{ node(ll t,ll c):to(t),cost(c){} ll to; ll cost; }; vector<node>g[M]; priority_queue<P,vector<P>,greater<P> > que; void add(ll u,ll v,ll c){ g[u].push_back(node(v,c)); g[v].push_back(node(u,c)); } void dij(){ for(ll i = 0;i <= n; i ++){ dist[i] = inf; } dist[1] = 0; que.push(P(0,1)); while(!que.empty()){ P p = que.top(); que.pop(); ll v = p.second; if(dist[v] < p.first) continue; for(ll i = 0;i < g[v].size();i ++){ node e = g[v][i]; ll cnt = 0; //cout<<e.to<<" "<<a[e.to]<<endl; if(a[e.to] > k) cnt = (a[e.to]-k)*(a[e.to]-k); if(dist[e.to] > dist[v]+e.cost+cnt){ dist[e.to] = dist[v] + e.cost + cnt; que.push(P(dist[e.to],e.to)); } } } } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll x,y,z; cin>>n>>m>>k; for(ll i = 1;i <= n;i ++) g[i].clear(); for(ll i = 1;i <= n;i ++){ cin>>a[i]; } k+=a[1]; for(ll i = 1;i <= m;i ++){ cin>>x>>y>>z; add(x,y,z); } dij(); cout<<dist[n]<<endl; }
I 起起落落
题目链接:https://www.cometoj.com/contest/7/problem/I?problem_id=100
思路:
把符合要求的序列的大小关系列一下会发现序列是会形成连续山峰状的,最简化的关系也就是 : a[i] > a[i+2] > a[i+1],符合序列可以为:(3,1,2) 因为i为偶数变大一点关系会成为: a[i] > a[i+2] > a[i+1] > a[i+4] > a[i+3] 符合序列可以为:(5,3 ,4,2,1),对于原序列,我们只要找到符合a[i] > a[i+2] > a[i+1]的三个数就算一个序列,如果这些序列的头尾有相连的,那么就会形成新的序列,我们用dp[i]表示以i结尾的子序列有多少个,然后从i向前遍历如果出现a[j] < a[i],那么j点可以作为三个点的中间点,记录k++,如果a[j] > a[i] 那么这个点可以作为三个点的开头点,可以和k个中间点组合形成k个新序列,如果以j为尾节点的序列数为dp[j],那么之前组成的新序列可以并上之前的序列,形成dp[j]*k个新节点,所以此时状态转移方程为:dp[i] += dp[j]*k + k;
实现代码;
#include<bits/stdc++.h> using namespace std; #define ll long long const int mod = 1e9+7; const int M = 2e3+10; ll dp[M],a[M]; int main() { int n; ll ans = 0; cin>>n; for(int i = 1;i <= n;i ++){ cin>>a[i]; } for(int i = 3;i <= n;i ++){ ll k = 0; for(int j = i-1;j >= 1;j --){ if(a[j] < a[i]) k++; else if(a[j] > a[i]) dp[i] = (dp[i]+(dp[j]+1)*k)%mod; } ans = (ans + dp[i])%mod; } cout<<ans<<endl; }
J 夺宝奇兵
题目链接:https://www.cometoj.com/contest/7/problem/J?problem_id=101
思路:枚举最终获得的物品的数量k,遍历一遍所有居民如果居民的物品数量x大于等于k那么买下该居民最便宜的x-k+1件物品,所有居民都买完后如果买到的物品小于预期值k,
那么就从剩下的所有物品中挑选最便宜的几件填进去。
之前好像在51nod上写扫描线专题的时候写到过基本一样的题。。。
#include<bits/stdc++.h> using namespace std; #define ll long long const ll M = 1e3 + 10; vector<ll>g[M]; const ll inf = 1e18; ll vis[M],f[M],p[M]; bool cmp1(ll x,ll y){ return p[x] < p[y]; } int main(){ ll n,m,to; ll mx=inf; cin>>n>>m; for(ll i = 1;i <= m;i ++){ cin>>p[i]>>to; f[i] = i; g[to].push_back(i); } for(ll i = 1;i <= n;i ++) sort(g[i].begin(),g[i].end(),cmp1); for(ll i = 1;i <= m;i ++){ ll cnt = 0;ll ans = 0; memset(vis,0,sizeof(vis)); for(ll j = 1;j <= n;j ++){ if(g[j].size() >= i){ for(ll k = 0;k <= g[j].size()-i;k ++){ ans += p[g[j][k]]; vis[g[j][k]]=1; cnt++; } } } if(cnt < i){ sort(f+1,f+1+m,cmp1); for(ll j = 1;j <= m;j ++){ if(!vis[f[j]]){ ans += p[f[j]]; cnt++; } if(cnt == i) break; } } mx = min(mx,ans); } cout<<mx<<endl; } /* 1 108 2 63 3 45 4 32 5 28 6 33 7 48 8 63 9 78 10 93 11 108 */