今天的题都有思路,但都搞不出来;
题解:
第一题:我看到数据范围想到了矩阵快速幂,实际上是不需要的,每次暴力乘组合数就好了,因为原来dp[i]对应前面多项,而这回dp[i]对应的dp[i-n]是唯一确定的;
确定了前n行,后面每一行放多少就唯一确定,只需要一直乘方案数;
#include<bits/stdc++.h> using namespace std; #define ll long long const int M = 105; const int mod=1e9 + 7; int c[M][M], dp[M][M*M], f[M][M]; ll ksm(ll a, ll b){ ll ret=1; for(;b;b>>=1,a=a*a%mod) if(b&1)ret=ret*a%mod; return ret; } inline int moc(int a){return a >= mod ? a - mod : a;} int main(){ freopen("chess.in","r",stdin); freopen("chess.out","w",stdout); int n, C; ll m; scanf("%d%lld%d", &n, &m, &C); for(int i = 0; i <= n; i++) for(int j = 0; j <= i; j++) if(j == 0 || i == j) c[i][j] = 1; else c[i][j] = moc(c[i-1][j] + c[i-1][j-1]); for(int i = 1; i <= n; i++) for(int j = 0; j <= n; j++) f[i][j] = ksm(c[n][j], (m-i)/n + 1); dp[0][0] = 1; for(int i = 1; i <= n; i++){ for(int j=0; j <= i*n && j <= C; j++){ int L = min(n, j); for(int t = 0; t <= L; t++){ dp[i][j] = moc(dp[i][j] + (ll)dp[i-1][j-t]*f[i][t]%mod); } } } printf("%d ", dp[n][C]); }
第二题:可以先用一个栈处理出每个K对应的右边第一个小于他的未知,中间的元素都>=a[k],所以只需要在中间找一个最大值且最靠左的位置,就可以最大K的最大贡献了,对应找最大值我用了一课线段数,时间空间都不行;
考虑在弹栈的时候一并解决,pos[t]表示从栈中t这个位置到栈前下一个位置这段区间中的最大值的位置,每次弹栈时就自上往下取最大的MAX,如果栈空了,就把之前的贡献都去掉;
我觉得我还是说不清楚,还是看代码吧,特别鸣谢:zjj
#include<bits/stdc++.h> using namespace std; const int M = 1e7 + 5; int n, a[M], q[M]; int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*=f; } int pos[M]; int main(){ freopen("array.in","r",stdin); freopen("array.out","w",stdout); scanf("%d", &n); for(int i = 1; i <= n; i++) a[i] = read(); a[n + 1] = 0; int ans = 0; int h = 1, t = 1; q[1] = 1; pos[1]=1; int mx = 0; for(int i = 2; i <= n + 1; i++){ int res= i - 1,mx = a[i - 1]; while(h <= t && a[i] < a[q[t]]){ ans = max(ans, res - q[t] + 1); if(a[pos[t]] == mx) res = max(q[t], res); else if(a[pos[t]] > mx) res = pos[t], mx = a[pos[t]]; t--; } if(h > t) { q[++t] = i; pos[t] = i; continue; } if(a[i] >= mx) { mx = a[i]; res = i; } q[++t] = i; pos[t] = res; } printf("%d ", ans); }
第三题:本来是莫队+线段树统计最长连续子段和,但我真的不知道那里写挂了;
正解:回滚莫队,只带增添不带删减的莫队,每次记录版本的答案,右边一直延展,左边一直回滚
#include<bits/stdc++.h> using namespace std; const int M = 1e5 + 5; int n, m, a[M], ans[M]; struct Query{int l, r, id;}q[M], qs[M]; bool cmp1(Query A, Query B){return A.l < B.l;} bool cmp2(Query A, Query B){return A.r < B.r;} int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*=f; } int L[M], R[M], vis[M], TTT, skt[M], tp, has[M], now_ans; void update(int t){ if(vis[t] != TTT) vis[t] = TTT, L[t] = R[t] = t, has[t] = 0; } void add(int t, bool f){ update(t), update(t+1), update(t-1); int lf=L[t],rg=R[t]; if(t!=1 && has[t-1]) lf=L[t-1]; if(t!=n && has[t+1]) rg=R[t+1]; now_ans = max(rg - lf + 1, now_ans); R[lf] = rg, L[rg] = lf; has[t] = 1; if(f) skt[++tp] = t; } void restore(){ while(tp){ int t=skt[tp];tp--; has[t] = 0; if(t!=1 && has[t-1]) R[L[t-1]]=t-1; if(t!=n && has[t+1]) L[R[t+1]]=t+1; } } int main(){ freopen("cc.in","r",stdin); freopen("ants.out","w",stdout); n = read(), m = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i <= m; i++) q[i].l = read(), q[i].r = read(), q[i].id = i; sort(q + 1, q + 1 + m, cmp1); int siz = sqrt(n + 0.5), now = 1; for(int i = 1, j = 1; i <= n && now <= m; i++){ int up = min(i * siz, n), tot = 0; while(now <= m && q[now].l <= up)qs[++tot] = q[now++]; if(!tot)continue; TTT++; sort(qs + 1, qs + 1 + tot, cmp2); int now_r = up; now_ans = 1; for(int j = 1; j <= tot; j++){ while(now_r < qs[j].r){ int t = a[++now_r]; add(t, 0); } int tmp = now_ans; for(int k = min(up, qs[j].r); k >= qs[j].l; k--) add(a[k], 1); ans[qs[j].id] = now_ans; restore(); now_ans = tmp; } } for(int i = 1; i <= m; i++) printf("%d ", ans[i]); }