C. Increase Subarray Sums
大意:
一共 n 个数,定义
f
(
k
)
f(k)
f(k) 为:将 k 个不同位置上的值都加上 x 后,能够得到的最大的连续子串和。
求
f
(
0...
k
)
f(0...k)
f(0...k)。
(
n
≤
5000
)
(n ≤5000)
(n≤5000)
思路:
将 k 个不同位置上的值都加上 x 后,最大的连续子串长度不一定为 k。
可以分别找到所有长度子串的最大和(前缀和处理),所用到的子串一定是这些中的一个。
从小到大枚举增加 x 的位置个数 k:
- 如果这 k 个位置都用完的话,最大值就是长度不少于 k 的子串中的最大和 + k*x。
- 如果不用完,最大值就是之前枚举得到的最大值。
两者取最大。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N], mlen[N], lastm[N];
int s[N];
signed main(){
Ios;
cin>>T;
while(T--)
{
cin >> n >> m;
for(int i=1;i<=n;i++) cin>>a[i], s[i]=s[i-1]+a[i];
for(int i=0;i<=n;i++)
{
int maxa=-1e9;
for(int j=i;j<=n;j++)
maxa = max(maxa, s[j]-s[j-i]);
mlen[i] = maxa;
}
lastm[n+1] = -1e9;
for(int i=n;i>=0;i--) //预处理出后缀最大值
lastm[i] = max(lastm[i+1], mlen[i]);
int maxa=0;
for(int i=0;i<=n;i++)
{
maxa = max(maxa, lastm[i] + i*m);
cout << maxa << ' ';
}
cout << endl;
}
return 0;
}
D. Cross Coloring
大意:
n*m 的矩阵,进行 q 次操作,每次可以挑选一个位置 (xi, yi) 和 k 种颜色中的一个,将其所在的行与列全部涂成该颜色。
问,最终矩阵的颜色一共有多少方案。
(
1
≤
n
,
m
,
k
,
q
≤
2
∗
1
0
5
)
(1 ≤n,m,k,q ≤2*10^5)
(1≤n,m,k,q≤2∗105)
思路:
找出整个矩阵被分成了几个部分,每个部分有 k 种选择,方案数就是
k
c
n
t
k^{cnt}
kcnt。
对于染色操作,后面的可能会覆盖住前面的,所以从后往前看。如果前面的行或者列后面出现过,就要归于后面。
从后往前走,如果当前位置所在的行或者列之前没有出现过,那么这是一个新的部分,cnt++,将当前行和列标记。
需要注意的是,如果行数集齐n个或者列数集齐m个了,说明整个矩阵都被标记了,前面的不用看了,break掉。
否则将会出现重复标记,比如从后往前(2,1),(2,2),将所有的列集齐了,那么前面的(1,1)就不能算了。
Code:
int qmi(int x, int y)
{
int ans=1;
while(y)
{
if(y&1) ans = ans*x%mod;
x = x*x%mod;
y >>= 1;
}
return ans;
}
signed main(){
Ios;
cin>>T;
while(T--)
{
int q;
cin>>n>>m>>k>>q;
for(int i=1;i<=q;i++) cin>>r[i]>>c[i];
row.clear(), col.clear();
int cnt=0;
for(int i=q;i>=1;i--)
{
if(row.count(r[i]) && col.count(c[i])) continue;
cnt++;
row.insert(r[i]), col.insert(c[i]);
if(row.size() == n || col.size() == m) break; //#如果所有的行或者列都集齐了,那所有位置都填满了,就不用往下看了。
}
cout << qmi(k, cnt) << endl;
}
return 0;
}