8.7 Round 2
刚说完白天思维性弱,晚上就来三道思维题...
T1:https://www.luogu.org/problem/T92604
规律没找出来,35pts打表滚粗
可以证明(我也不会),p=a^3+3a+1
在a=871时,p>2e9,所以枚举a,然后sqrt(L)判p是否是质数即可
T2:https://www.luogu.org/problem/T92605
考场上想了一想,觉得sum不好维护,于是就暴力了...
题目中给的y = -1或1,就保证了,对一个数进行修改,只会影响它前后一位的sum
做法:统计sum[i]:<=i的数的个数和,num[i],=i的数的个数和
然后对于一个数,它不可以删,当且仅当num[i] != 0 && sum[i] != i
记上面的答案为ok[i],线段树维护序列,于是我们获得了一个权值线段树,值为0/1
对于询问,相当于询问1-x的最靠右1的位置,轻松维护
对于修改,考虑几种情况就行
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<climits> #include<map> #include<queue> #include<cmath> using namespace std; #define O(x) cout << #x << " " << x << endl; #define B cout << "breakpoint" << endl; #define clr(a) memset(a,0,sizeof(a)); inline int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (ans *= 10) += ch - '0'; ch = getchar(); } return ans * op; } const int maxn = 5e5 + 5; int a[maxn],n,m,tp[maxn]; int num[maxn],sum[maxn]; bool ok[maxn]; struct node { int sum,rm; }; struct seg_tr { node tr[maxn << 2]; #define ls i << 1 #define rs i << 1 | 1 inline node up(node i,node l,node r) { i.sum = l.sum + r.sum; if(r.rm) i.rm = r.rm; else i.rm = l.rm; return i; } void build(int i,int l,int r) { if(l == r) { if(ok[l]) tr[i] = (node){1,l}; else tr[i] = (node){0,0}; return; } int mid = l + r >> 1; build(ls,l,mid); build(rs,mid + 1,r); tr[i] = up(tr[i],tr[ls],tr[rs]); } void change(int i,int l,int r,int x,int d) { if(l == r) { tr[i].sum = d; if(d) tr[i].rm = l; else tr[i].rm = 0; return; } int mid = l + r >> 1; if(x <= mid) change(ls,l,mid,x,d); else change(rs,mid + 1,r,x,d); tr[i] = up(tr[i],tr[ls],tr[rs]); } node query(int i,int l,int r,int ql,int qr) { if(l == ql && r == qr) return tr[i]; int mid = l + r >> 1; if(qr <= mid) return query(ls,l,mid,ql,qr); else if(ql > mid) return query(rs,mid + 1,r,ql,qr); else { node tr1 = query(ls,l,mid,ql,mid),tr2 = query(rs,mid + 1,r,mid + 1,qr); node res = up(res,tr1,tr2); return res; } } }T; int main() { freopen("seq.in","r",stdin); freopen("seq.out","w",stdout); n = read(),m = read(); for(int i = 1;i <= n;i++) a[i] = read(),num[a[i]]++; for(int i = 1;i <= n;i++) { sum[i] = sum[i - 1] + num[i]; if(sum[i] != i && num[i]) ok[i] = 1; } //for(int i = 1;i <= n;i++) printf("%d ",ok[i]); //cout << endl; T.build(1,1,n); while(m--) { int op = read(); if(op == 1) { int x = read(); node ans = T.query(1,1,n,1,x); if(!ans.sum) { printf("0\n"); } else printf("%d\n",sum[ans.rm]); } else { int x = read(),y = read(); int p = a[x]; a[x] += y; if(y == -1) { sum[p - 1]++; if(sum[p] - sum[p - 1] == 0) T.change(1,1,n,p,0); if(sum[p - 1] == p - 1) T.change(1,1,n,p - 1,0); else T.change(1,1,n,p - 1,1); } else { sum[p]--; if(sum[p] - sum[p - 1] == 0) T.change(1,1,n,p,0); else { if(sum[p] == p) T.change(1,1,n,p,0); else T.change(1,1,n,p,1); } if(sum[p + 1] == p + 1) T.change(1,1,n,p + 1,0); else T.change(1,1,n,p + 1,1); } } } } /* 6 7 2 3 4 6 6 6 1 6 2 4 -1 2 4 -1 1 6 1 4 2 2 -1 1 6 */
T3:https://www.luogu.org/problem/T92606
考试的时候净做这题去了,结果树dp写的不好,一看这复杂度不爆炸?于是放弃
一开始的想法:f[u][i][j]:u及u的子树,分配i个1,号,j个2号的最多覆盖点
那么先枚举u分配情况,再dp子树v,然后再给v分配水泵做dp,过程中临时数组记录
第二问就二分,在分配给u水泵的时候使分配方案合法即可
这样O(T*n*200^3*logai),GG
然后看了标程恍然大悟
在处理u的时候,我们可以先处理v,更新f数组
在u的所有子树被处理完之后,最后再给u分配水泵
加上临时数组,也很好写
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<climits> #include<map> #include<queue> #include<cmath> using namespace std; #define O(x) cout << #x << " " << x << endl; #define B cout << "breakpoint" << endl; #define clr(a) memset(a,0,sizeof(a)); #define int long long inline int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (ans *= 10) += ch - '0'; ch = getchar(); } return ans * op; } const int maxn = 25; const int M = 55; struct egde { int to,next; }e[maxn << 1]; int fir[maxn],alloc; void adde(int u,int v) { e[++alloc].next = fir[u],fir[u] = alloc,e[alloc].to = v; swap(u,v); e[++alloc].next = fir[u],fir[u] = alloc,e[alloc].to = v; } int n,a[maxn],s1,s2,p1,p2;//p1 > p2 int f[25][M][M],tmp[M][M]; int mid,ans; void dp(int u,int fa) { for(int i = fir[u];i;i = e[i].next) { int v = e[i].to; if(v == fa) continue; dp(v,u); clr(tmp); for(int i = 0;i <= s1;i++) for(int j = 0;j <= s2;j++) for(int x = 0;x <= i;x++) for(int y = 0;y <= j;y++) tmp[i][j] = max(tmp[i][j],f[u][i - x][j - y] + f[v][x][y]); for(int i = 0;i <= s1;i++) for(int j = 0;j <= s2;j++) f[u][i][j] = tmp[i][j]; } clr(tmp); for(int i = 0;i <= s1;i++) for(int j = 0;j <= s2;j++) for(int x = 0;x <= i;x++) for(int y = 0;y <= j;y++) if(x * p1 + y * p2 >= a[u] && x * p1 + y * p2 - a[u] <= mid) tmp[i][j] = max(tmp[i][j],f[u][i - x][j - y] + 1); for(int i = 0;i <= s1;i++) for(int j = 0;j <= s2;j++) f[u][i][j] = tmp[i][j]; } bool solve() { clr(f); dp(1,0); int res = 0; for(int i = 0;i <= s1;i++) for(int j = 0;j <= s2;j++) res = max(res,f[1][i][j]); return res == ans; } main() { freopen("system.in","r",stdin); freopen("system.out","w",stdout); int t = read(); while(t--) { int n = read(); clr(fir); alloc = 0; for(int i = 1;i <= n;i++) a[i] = read(); for(int i = 1;i < n;i++) { int u = read(),v = read(); adde(u,v); } s1 = read(),s2 = read(),p1 = read(),p2 = read(); mid = 1000000000; clr(f); dp(1,0); ans = 0; for(int i = 0;i <= s1;i++) for(int j = 0;j <= s2;j++) ans = max(ans,f[1][i][j]); printf("%lld ",ans); int l = 0,r = 1000000000,prel = 0,prer = 0; int res = 0; while(l < r) { mid = (l + r) >> 1ll; if(solve()) r = mid,res = mid; else l = mid + 1; if(l == prel && r == prer) res = r,r--; prel = l,prer = r; } printf("%lld\n",res); } } /* 1 4 5 30 20 20 1 2 1 3 3 4 4 1 10 15 */