A 签到
C 思维或者DP
D 打表 数据结构 树状数组
H 树形DP
I DP,几何
J 记忆化搜索
M 思维
E 线段树维护矩阵,待补
Oops, It's Yesterday Twice More
初始的时候全部格子都有袋鼠,让最终全部袋鼠都落在一个格子里面,移动步数不能超过3*(n-1)。考虑全部袋鼠先移动到同一个距离a,b最近的角落,花费不会超过2*(n-1)。每个最近的角落到袋鼠最多只有(n-1)次移动
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { int n, a, b; cin >> n >> a >> b; if (a * 2 <= n && b * 2 <= n) { for (int i = 1; i < n; i++) cout << 'L'; for (int i = 1; i < n; i++) cout << "U"; for (int i = 1; i < a; i++) cout << "D"; for (int i = 1; i < b; i++) cout << "R"; } else if (a * 2 > n && b * 2 <= n) { for (int i = 1; i < n; i++) cout << 'L'; for (int i = 1; i < n; i++) cout << "D"; for (int i = n; i > a; i--) cout << "U"; for (int i = 1; i < b; i++) cout << "R"; } else if (a * 2 <= n && b * 2 > n) { for (int i = 1; i < n; i++) cout << 'U'; for (int i = 1; i < n; i++) cout << "R"; for (int i = 1; i < a; i++) cout << "D"; for (int i = n; i > b; i--) cout << "L"; } else { for (int i = 1; i < n; i++) cout << 'D'; for (int i = 1; i < n; i++) cout << "R"; for (int i = n; i > a; i--) cout << "U"; for (int i = n; i > b; i--) cout << "L"; } return 0; }
赛时用的dp, 因为这种只进行一次区间操作的模型是很经典的。由于答案最多只涉及两种数字,一种是最终确定答案数量的x,一种是能够变成x的x-k,对这两种数字做dp, dp[0]代表当前没进行+k操作, dp[1]代表当前正在进行+k操作,dp[2]代表+k操作已经进行完。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e6 + 7; int pos[4000000+10]; int dp[1000000 + 10][2][3], a[1000000 + 10]; int cnt[4000000+10]; int main() { int n; cin >> n; int k; cin >> k; int fuck=2000000; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); a[i]+=fuck; } if(k==0) { int ans=0; for(int i=1; i<=n; i++) { cnt[a[i]]++; ans=max(ans,cnt[a[i]]); } cout<<ans; return 0; } int ans = 0; for (int i = 1; i <= n; i++) { int pre = a[i] - k; int prepos = max(pos[pre], pos[a[i]]); int flag=1; if(prepos==pos[pre]) flag=0; dp[i][1][0] = dp[prepos][flag][0] + 1; dp[i][1][1] = max(dp[prepos][flag][0], dp[prepos][flag][1]); dp[i][1][2] = max(dp[prepos][flag][1], dp[prepos][flag][2]) + 1; ans = max(ans, dp[i][1][0]); ans = max(ans, dp[i][1][1]); ans = max(ans, dp[i][1][2]); pre = a[i] + k; prepos = max(pos[pre], pos[a[i]]); flag=0; if(prepos==pos[pre]) flag=1; dp[i][0][0] = dp[prepos][flag][0]; dp[i][0][1] = max(dp[prepos][flag][0], dp[prepos][flag][1]) + 1; dp[i][0][2] = max(dp[prepos][flag][1], dp[prepos][flag][2]); pos[a[i]] = i; ans = max(ans, dp[i][0][0]); ans = max(ans, dp[i][0][1]); ans = max(ans, dp[i][0][2]); } cout << ans; }
打表题。还是很难发现规律的,特别是第三点。考虑当前ans[i]由ans[i-1]递推。 先打一个正常的排列观察答案。再在末尾加不同的一个数字,发现加的数字x=max(a[1]...a[i-1])的时候,ans[i]=ans[i-1], <max(a[1],.....a[i-1])的时候,ans[i]=ans[i-1]+cnt(比x大的数字去重后个数) , >max(a[1]....a[i-1])的时候,是ans[i]=ans[i-1]+2+(i-1-pos2+1) pos2指前面最大值第二次出现的位置与i-1的位置。
这一过程可以用树状数组实现
#include<bits/stdc++.h> using namespace std; typedef long long int ll; int lowbit(int x) { return x&-x; } int book[1000000+10]; int sum[1000000+10],n,a[100000+10]; void change(int x,int val) { while(x<=n) { sum[x]+=val; x+=lowbit(x); } } int getans(int pos) { int ans=0; while(pos) { ans+=sum[pos]; pos-=lowbit(pos); } return ans; } ll ans[100000+10]; int main() { /* 当a[i]<max(a[1],a....a[i-1])的时候,ans[i]=ans[i-1]+比a[i]大的数字个数(去重) 当a[i]=max(a[1],,,.a[i-1])的时候,ans[i]=ans[i-1] 当a[i]>max(a[i].....a[i-1])的时候,ans[i]=ans[i-1]+2+ dis(pos2,i-1) */ int t; cin>>t; while(t--) { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } int maxx=0,pos2=0; for(int i=1;i<=n;i++) { if(a[i]==maxx) { ans[i]=ans[i-1]; } else if(a[i]<maxx) { ans[i]=ans[i-1]+getans(n)-getans(a[i]); } else { ans[i]=ans[i-1]+2; if(pos2) { ans[i]+=(i-1-pos2+1); } } if(i==1) ans[i]=0; if(maxx<a[i]) { maxx=a[i]; pos2=0; } else if(maxx==a[i]) { if(pos2==0) pos2=i; } if(book[a[i]]==0) { book[a[i]]=1; change(a[i],1); } } for(int i=1;i<=n;i++) { if(i!=n) cout<<ans[i]<<" "; else cout<<ans[i]; } cout<<'\n'; ans[0]=0; for(int i=1;i<=n;i++) { if(book[a[i]]) change(a[i],-1); ans[i]=0; book[a[i]]=0; } } return 0; }
当一个点选择当前儿子的时候,发现最多只能选择两个儿子,且其中一个必须是t=3的。
选择两个儿子时,第一个走到的儿子节点x,x的全部儿子一定会在走当前另一个儿子的时候跑光。因此设置一个dp0[i]代表i全部儿子跑光的价值,dp0[i]只计算全部儿子dp1值的和,不算儿子val的和。 对于当前节点的另外一个儿子算dp1值与val值,剩余的只算dp1值。显然不能暴力枚举选择情况,考虑总体先加上全部儿子的dp值,记录前二大的,dp0+val-dp1的节点
枚举t=3的节点,当该节点是最大的dp0+val-dp1时,选择次大的,否则选择最大的
选择一个儿子时,就是一个dp1+val最大值加上剩余儿子的dp1值
不选儿子时,就是儿子dp的累加即可
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5 + 5; inline int read() { int x = 0, f = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); } while (isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x * f; } inline void write(ll x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); } int head[N], n, to[N << 1], nxt[N << 1], cnt; int ti[N]; ll f[N], f1[N], val[N]; void init() { cnt = 0; for (int i = 0; i <= n; i++) { head[i] = 0; f[i] = f1[i] = 0; } } void add(int u, int v) { to[++cnt] = v; nxt[cnt] = head[u]; head[u] = cnt; } void dfs(int u, int fa) { ll mxval = 0, sumval = 0; pair<int, ll> a[2]; a[0].first = 0, a[0].second = 1e18, a[1].first = 0, a[1].second = 1e18; int ssum = 0; for (int i = head[u]; i; i = nxt[i]) { int v = to[i]; if (v == fa) continue; ssum++; dfs(v, u); ll c = f[v] - (f1[v] + val[v]); pair<int, ll> now; now.first = v, now.second = c; for (int j = 0; j < 2; j++) { if (now.second < a[j].second) { pair<int, ll> it; it = now; now = a[j]; a[j] = it; } } mxval = max(mxval, val[v]); sumval += f[v]; f[u] += f[v]; f1[u] += max(f[v], f1[v]); } f[u] += mxval; if (ssum > 1) for (int i = head[u]; i; i = nxt[i]) { int v = to[i]; if (v == fa) continue; int idx = 0; if (v == a[0].first) idx++; if (ti[v] == 3) { f[u] = max(f[u], val[v] + sumval - f[a[idx].first] + f1[a[idx].first] + val[a[idx].first]); } } } int main() { int t = read(); while (t--) { n = read(); init(); for (int i = 1; i <= n; i++) val[i] = read(); for (int i = 1; i <= n; i++) ti[i] = read(); for (int u, v, i = 1; i < n; i++) { u = read(), v = read(); add(u, v), add(v, u); } dfs(1, 0); write(f[1] + val[1]); puts(""); } return 0; }
倒着dp,终点设为dp[0], 在同一条下行对角线的点满足(x+y)%2h=k,在同一条上行对角线的点满足(x-y)%2h=k, 至于为什么是2h而非h,因为极端情况从原点出发到顶部结束再下行到x轴(x+y)=2h,如果用h的话,会有点的重复。
按照x坐标从大到小排序,当前为点的时候,因为点不能反射,所以直接在其所在的上行与下行对角线++即可
如果是镜子,就对两个方向取max
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10; map<int,int>mp; struct node { int flag,x,y; }; struct node s[5000000+10]; bool cmp(struct node a, struct node b) { return a.x>b.x; } int main() { int t; cin>>t; while(t--) { int n,h; cin>>h; cin>>n; mp.clear(); for(int i=1; i<=n; i++) { scanf("%d%d",&s[i].x,&s[i].y); s[i].flag=1; } int m; cin>>m; for(int i=n+1; i<=n+m; i++) { s[i].flag=0; scanf("%d%d",&s[i].x,&s[i].y); } sort(s+1,s+1+n+m,cmp); for(int i=1; i<=m+n; i++) { if(s[i].flag==0) { int nowshang=(s[i].x-s[i].y)%(2*h); int nowxia=(s[i].x+s[i].y)%(2*h); mp[nowshang]++; mp[nowxia]++; } else { int nowshang=(s[i].x-s[i].y)%(2*h); int nowxia=(s[i].x+s[i].y)%(2*h); mp[nowshang]=mp[nowxia]=max(mp[nowshang],mp[nowxia]); } } cout<<mp[0]<<'\n'; } return 0; }
a%p=0,b%p=0 则(a-b)%p=0,反之亦然
又因为+1,-1差值不变,所以a,b能够整除的p始终就是那几十个数字
直接暴力记忆化,这里hash一下 (a,b)的状态,把二维map变成一维的
在a,b变化的时候,枚举每个质数,获取余数mod, 考虑向下或者向上进入这一两种状态。
但这样会t,这时候贪心的想,直接在进入两种状态之前就除,而不是再继续扩展。
#include<bits/stdc++.h> using namespace std; typedef long long int ll; ll gethash(ll x,ll y) { return x*1e9+y; } unordered_map<ll,int>mp; ll m[1000],len; ll dfs(ll a,ll b) { if(a==1||b==1) return 0; ll nowhash=gethash(a,b); if(mp[nowhash]) return mp[nowhash]; ll nowminn=min(a-1,b-1); for(int i=1;i<=len;i++) { ll nowm=a%m[i]; ll tempa=a-nowm; ll tempb=b-nowm; if(tempa%m[i]==0&&tempb%m[i]==0) nowminn=min(nowminn,nowm+dfs(tempa/m[i],tempb/m[i])+1); if((a+m[i]-nowm)%m[i]==0&&(b+m[i]-nowm)%m[i]==0) { nowminn=min(nowminn,m[i]-nowm+1+dfs((a+m[i]-nowm)/m[i],(b+m[i]-nowm)/m[i])); } } return mp[gethash(a,b)]=nowminn; } int main() { /* ab同时除以p a%p=0 b%p=0 (a-b)%p=0 ab无论怎么变化,其差值不变,能除以的数字是固定的 */ int t; cin>>t; while(t--) { // mp.clear(); ll a,b; cin>>a>>b; ll cha=abs(a-b); len=0; for(ll i=2;i*i<=cha;i++) { if(cha%i==0) { len++; m[len]=i; while(cha%i==0) cha/=i; } } if(cha>1) { len++; m[len]=cha; } cout<<dfs(a,b)<<'\n'; } return 0; }
有正有负的时候,留一个负数和与之相连的正数,用负数减去全部的正数,在此之前,正数可以吸取合并相邻负数获得更大正数。然后我们用最后一个正数去减去这个减了全部数字的负数,最终就是全部绝对值
只有正数的时候,考虑一个正数减去剩下全部正数,最后剩下两个数,用正减去负
只有负数的时候,考虑一个负数减去剩下全部负数,只剩一个正数
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e6 + 7; ll q[N]; ll c[N]; int main() { int t; scanf("%d", &t); while (t--) { int n; scanf("%d", &n); ll s = 0; int flag=0; for (int i = 1; i <= n; i++) { scanf("%lld", &q[i]); s += abs(q[i]); if(q[i]>0) flag=1; } for (int i = 1; i <= 4; i++) { q[n + i] = q[i]; } if (n == 1) { printf("%lld\n", q[1]); } else if (n == 2) { printf("%lld\n", max(q[1] - q[2], q[2] - q[1])); } else if (n == 3) { ll mx = -1e18; for (int i = 1; i <= 3; i++) { mx = max(mx, q[i] - q[i + 1] + q[i + 2]); mx = max(mx, q[i] - q[i + 1] - q[i + 2]); } printf("%lld\n", mx); } else { ll mx; if(flag) { mx = -1e18; for (int i = 1; i <= n; i++) { mx = max(mx, s - abs(q[i]) - abs(q[i + 1]) + q[i] - q[i + 1]); } } else { mx=-1e18; for(int i=1; i<=n; i++) { mx=max(mx,s-abs(q[i])+q[i]); } } printf("%lld\n", mx); } } }
文章评论