摆了一天,非常困,啥也没写,大部分不是口胡就是看题解。
打球
MX noip Round1
T1
很快就会了,没修改的时候最优的肯定是最大值加上最小的,直接排序求最大最小值即可。
而对于修改 \(a_x=v\) ,不妨设修改是变小的:
就二分出 \(v\) 的位置 \(pos\) ,对于 \([1,pos),(x,n]\) 没改变,而对于 \([pos,x]\) 就 预处理一个错位的 \(st\) 表和原来两边的答案取最值。
变大是一样的总共维护 \(4\) 个就好,不想写了。
T2
发现对于答案有贡献的就是数组里的 \(1和2\) ,因为 \(k\) 是可以随便选的。
正难则反,考虑什么样的区间是不合法的 (\(f=0\))
1、没有 \(1\) 的区间,所以对于一个极大的这个区间长度为 \(len\) ,贡献是 \(\frac{n*(n-1)}{2}\)
2、两端是 \(1\) 中间没有 \(2\) 的区间
所以用两个 \(set\) 分别维护 \(1,2\) 的位置集合,二分各种加减贡献。
具体来说对于将 \(a_x=v\)
1、\(a_x=1\) :
二分出前一个和后一个 \(1\) 的位置 \(l,r\),考虑减去 \([l,x],[x,r]\) 的,加上 \([l,r]\)
再判断 \([l,x],[x,r]\) 的 \(2\) 的个数,从而判断 \([l,r]\) 是否符合条件二,可以再二分也可以用一个树状数组维护
2、\(a_x=2\)
找 \(x\) 两端最近的 \(1\) 看看是不是删完之后就没有 \(2\) 了。
\(v=1/2\) 的都是类似的,也没写代码,真的很摆......
与 csp 相关
arc207a
似乎与 \(T4\) 相关?
学会了一类 \(trick\)
对于复杂的题意,尤其是 \(dp\) ,考虑形式化题意变成一个柿子
而这题就是对于 \(a\) 的一个排列 \(b\) ,要求:
\(\sum max(b_i-(i-1),0) \le X\)
第一个 trick
发现 \(max(b_i-(i-1),0)+min(b_i,i-1)=b_i\) ,我也不知道怎么发现的,反正观察题解就发现了。
所以设 \(Y=\sum a_i - X\) 则要求 \(min(b_i,i-1) \ge Y\)
第二个 trick
统计这类排列的数量,利用二分图匹配。
因为首先大脑就会想到大的 \(b_i\) 和 大的 \(i-1\) 显然更好,所以考虑把 \(b\) 作为左部点,\(i-1\) 作为右部点,边权就是点权 \(min\) ,考虑从大到小排序之后顺序考虑每一个点。
对于每个点,要么与之前没被匹配的点匹配要么先不管等着后面来匹配,也许叫做贡献延后计算。
dp
所以设 \(f_{i,j,k,s}\) 表示考虑前 \(i\) 个点,有 \(j,k\) 个左右部点没匹配,权值和为 \(s\)
注意为什么现在可以在状态里写边权和 ,因为 \(\sum min(b_i,i-1) \le \frac{n*(n-1)}{2}\) ,所以 \(s\) 是 \(n^2\) 的。
那么分类这个点是左还是右,匹不匹配即可,现在是 \(O(n^5)\) 的
那么发现对于前 \(i\) 个点左右部点的数量固定,那么枚举了 \(j\) ,实际上 \(k\) 是固定的,就消去这一维,\(O(n^4)\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=202,mod=998244353;
int n,S,x,a[N],f[N][N][5050];
struct ed{int v,id;
}p[N];
bool cmp(ed x,ed y){return x.v>y.v;
}
int sum[N][2];
void add(int &x,int y){x+=y;if(x>mod)x-=mod;
}
signed main(){cin>>n>>x;for(int i=1;i<=n;i++){cin>>a[i];S+=a[i];p[i]={a[i],0},p[i+n]={i-1,1};}x=S-x;if(x<0){int ans=1;for(int i=1;i<=n;i++){ans=ans*i%mod;}cout<<ans;return 0;}if(x>(n-1)*n/2){cout<<0;return 0;}sort(p+1,p+2*n+1,cmp);for(int i=1;i<=2*n;i++){sum[i][0]=sum[i-1][0]+(!p[i].id);sum[i][1]=sum[i-1][1]+p[i].id;}f[0][0][0]=1;for(int i=1;i<=2*n;i++){int v=p[i].v;for(int j=0;j<=sum[i][0];j++){int k=sum[i][1]-sum[i][0]+j;if(k<0||k>sum[i][1])continue;for(int su=0;su<=n*(n-1)/2;su++){if(p[i].id){if(su>=v)add(f[i][j][su],(j+1)*f[i-1][j+1][su-v]%mod);if(k) add(f[i][j][su],f[i-1][j][su]);}else{if(su>=v)add(f[i][j][su],(k+1)*f[i-1][j][su-v]%mod);if(j)add(f[i][j][su],f[i-1][j-1][su]);}}}}int ans=0;for(int su=x;su<=n*(n-1)/2;su++) add(ans,f[n*2][0][su]);cout<<ans;return 0;
}