20210925 noip61
res
期望:rk5 30+20+20+70
实际:rk22 30+20+20+0
T4 \(a_i\) 可能 \(<0\),因此 DP 数组的初值要设为 \(-\inf\)
T4 sub4 数据锅了
rk1 szs 100+100+0+100
rk5 ycx 100+40+0+0
rk9 zjj 30+100+0+0
rk14 ys 30+20+0+50
rk14 zkx 0+100+0+0
交通
是个套路
把原图中每个点 \(i\) 出边所连的点 \(u_i,v_i\) 在新图中连边
原图中每个点的出入度都是 \(2\),因此新图中每个点的度数为 \(2\),一定会形成若干个偶环
留下原图中的边 \((i,u_i)\) 就等价于新图中边 \((u_i,v_i)\) 指向 \(u_i\)。
由于要求最后每个点入度为 \(1\),因此新图中一个点有且仅有一个连边指向它,即每个偶环只有 \(2\) 种方案,答案即为 \(2^{cnt}\),\(cnt\) 为偶环数,并查集维护即可。
code
const int N = 1e5+5, mod = 998244353;
int n,a[N],b[N];
int cnt,fa[N];
LL Pow(LL x,LL y)
{ LL res=1; for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod; return res; }
int find(int x) { return fa[x]==x ? x : fa[x]=find(fa[x]); }
signed main() {
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
read(n);
For(i,1,2*n) { int x,y; read(x,y); (!a[x] ? a[x] : b[x]) = y; }
For(i,1,n) fa[i] = i;
For(i,1,n) fa[find(a[i])] = find(b[i]);
For(i,1,n) cnt += find(i) == i;
write(Pow(2,cnt));
return iocl();
}
小P的单调数列
结论:答案序列最多划分成 \(2\) 个极长单调区间
证明:从平均的角度考虑,划分成 \(3\) 个的序列一定不比它扔掉 \(1\) 或 \(2\) 个后的子序列优。
这样只要正着倒着各跑一次带权 LIS 即可,时间复杂度 \(O(n\log n)\)
code
const int N = 1e5+5;
int n,a[N];
int mx,lsh[N];
LL f[N],g[N];
double ans;
struct BIT {
LL t[N];
void clear() { memset(t,0,sizeof t); }
void add(int i,LL x) { for(;i<=mx;i+=i&-i) ckmax(t[i],x); }
LL query(int i) { LL res=0; for(;i;i-=i&-i) ckmax(res,t[i]); return res; }
} bit;
signed main() {
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
read(n);
For(i,1,n) read(a[i]), lsh[i] = a[i];
sort(lsh+1,lsh+n+1), mx = unique(lsh+1,lsh+n+1)-lsh-1;
For(i,1,n) {
a[i] = lower_bound(lsh+1,lsh+mx+1,a[i])-lsh;
f[i] = bit.query(a[i]-1)+lsh[a[i]], bit.add(a[i],f[i]);
ckmax(f[i],f[i-1]), ckmax(ans,(double)f[i]);
}
bit.clear();
rFor(i,n,1) {
g[i] = bit.query(a[i])+lsh[a[i]], bit.add(a[i],g[i]);
ckmax(g[i],g[i+1]), ckmax(ans,(f[i-1]+g[i])/2.0);
}
printf("%.3lf",ans);
return 0;
}
矩阵
对于一个 \(3\times3\) 的矩阵,在操作过程中 \(a_{1,1}-a_{1,2}-a_{2,1}+a_{2,3}+a_{3,2}-a_{3,3}\) 的值一定,因此将前两行、前两列消成 \(0\) 后若 \(a_{3,3}\ne0\) 则一定无解。
这样只要将给定的矩阵前两行、前两列消成 \(0\) 即可,实际只需不到 \(2(n+m)\) 次操作
code
#define MT make_tuple
const int N = 1e3+5;
int n,m;
LL a[N][N];
vector<tuple<int,int,LL>> ans;
void add1(int i,LL x) { if( x ) {
ans.pb(MT(1,i,x));
For(j,1,m) a[i][j] += x;
}}
void add2(int j,LL x) { if( x ) {
ans.pb(MT(2,j,x));
For(i,1,n) a[i][j] += x;
}}
void add3(int k,LL x) { if( x ) {
ans.pb(MT(3,k,x));
for(int i = 1; i <= n && i+k <= m; ++i) if( i+k > 0 ) a[i][i+k] += x;
}}
signed main() {
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
read(n,m); For(i,1,n) For(j,1,m) read(a[i][j]);
For(j,1,m) add3(j-1,a[2][j]-a[1][j]), add2(j,-a[1][j]);
For(i,3,n) add1(i,-a[i][2]), add3(1-i,-a[i][1]);
For(i,3,n) For(j,3,m) if( a[i][j] ) puts("-1"), exit(0);
write(ans.size());
for(auto i : ans) write(get<0>(i),' '), write(get<1>(i),' '), write(get<2>(i));
return iocl();
}
花瓶
\(O(n^3)\) 暴力 DP 显然:设 \(s\) 为 \(a\) 前缀和,\(f[i,j]\) 为前 \(i\) 个花瓶,最后一段为 \([j+1,i]\) 的最大值,转移:
明显的斜率优化,可以拆成
枚举 \(j\),用 \(k<j\) 构建凸包,用 \(i\) 在上面查询
问题是 \(s\) 不单调,而 \(O(n^2\log n)\) 并不能通过。
那就以 \(s[k]\) 递增的顺序构建凸包,以 \(s[i]\) 递减的顺序查询,这样就可以贪心地从凸包前面删点,单调队列维护即可。
时间复杂度 \(O(n^2)\)
code
const int N = 5e3+5;
int n,a[N];
LL s[N],f[N][N];
VI id;
int l,r,q[N];
LD K(int i,int j,int k) {
if( s[i] == s[j] ) return f[k][i]>f[k][j] ? -1e15 : 1e15;
return LD(f[k][i]-f[k][j]) / (s[i]-s[j]);
}
signed main() {
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
read(n); For(i,1,n) read(a[i]), s[i] = s[i-1] + a[i];
memset(f,0xcf,sizeof f);
For(i,0,n) f[i][0] = 0, id.pb(i);
sort(id.begin(),id.end(),[](const int &x,const int &y){return s[x]<s[y];});
For(j,1,n) {
l = 1, r = 0;
for(int k : id) if( k < j ) {
while( l<r && K(q[r],k,j) > K(q[r-1],q[r],j) ) --r;
q[++r] = k;
}
for(auto it = id.rbegin(); it != id.rend(); ++it) {
int i = *it;
if( i > j ) {
while( l<r && s[i]-s[j] < K(q[l],q[l+1],j) ) ++l;
ckmax(f[i][j],(s[j]-s[i])*s[q[l]]+f[j][q[l]]+s[i]*s[j]-s[j]*s[j]);
}
}
}
write(*max_element(f[n],f[n]+n+1));
return iocl();
}