海亮02/18杂题
海亮02/18杂题
T1
题意
给你一个长度为 \(n\) 的数列,然后给你 \(q\) 个交换或不交换操作,你可以选择操作或者不操作,问所有情况下逆序对的总和。
答案需要对 \(10 ^ 9 + 7\) 取模。
\(n\leq 3000\),\(q\leq 3000\)。
题解
发现一个问题,对于操作执不执行很难描述,怎么办?
我们考虑设 \(f(i,j)\) 表示 \(a_i>a_j\) 的情况占总情况数的比例(其实就是看、作期望啦),然后在最后把总情况数 \(2^q\) 乘一下即可。
然后考虑每个操作对 \(f\) 的影响。
不难发现,有影响的只有 \(f(x,i)\)(当然还有 \(f(i,x)\))和 \(f(y,i)\)(当然还有 \(f(i,y)\))。
发现影响是 \(O(n)\) 的,直接更新即可。
然后就做完了,整道题最难的点就是第一步转化拆贡献。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
bool stmemory;
namespace Call_me_Eric{
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 5e3 + 10, mod = 1e9 + 7;
int n, a, b;
int f[maxn][maxn][2], sum[maxn][maxn][2];
int qpow(int x,int a){
int res = 1;
while(a){
if(a & 1){res = res * x % mod;}
x = x * x % mod;a >>= 1;
}
return res;
}
int pw[maxn], pre[maxn];
void main(){
n = read(); a = read(); b = read();if(a < b)swap(a, b);
pw[0] = 1;for(int i = 1;i < maxn;i++){pw[i] = pw[i - 1] * 2 % mod;}
pre[0] = f[0][0][0] = f[0][0][1] = sum[0][0][0] = sum[0][0][1] = 1;
int ans = 0;
for(int i = 1;i <= n;i++){
sum[i][0][1] = f[i][0][1] = (pre[i - 1] - (i - b >= 0 ? pre[i - b] : 0) + mod) % mod;
for(int j = 1;j <= i;j++){
if(j >= b){
int val = sum[i - b][j - b][0];
if(j >= a)ans = (ans + val * pw[max(n - i - 1,0ll)] % mod) % mod;
else f[i][j][1] = val;
}
}
for(int j = 1;j <= i;j++){
int val = sum[i - 1][j - 1][1];
if(j >= a)ans = (ans + val * pw[max(n - i - 1,0ll)] % mod) % mod;
else f[i][j][0] = val;
}
pre[i] = pre[i - 1];
for(int j = 1;j <= i;j++){
pre[i] = (pre[i] + f[i][j][0]) % mod;
sum[i][j][0] = (sum[i - 1][j - 1][0] + f[i][j][0]) % mod;
sum[i][j][1] = (sum[i - 1][j - 1][1] + f[i][j][1]) % mod;
}
}
printf("%lld\n",ans);
return;
}
};
bool edmemory;
signed main(){
auto stclock = clock();
Call_me_Eric::main();
auto edclock = clock();
cerr << (&stmemory - &edmemory) / 1024.0 / 1024.0 << " Mib cost.\n";
cerr << (edclock - stclock) * 1.0 / CLOCKS_PER_SEC << " Sec cost.\n";
return 0;
}
T2
题意
Snuke 君有长为 \(N\) 的字符串 \(x\),最初 \(x\) 的所有字符都是 \(0\)。Snuke 君可以按照任意顺序进行任意次数以下两种操作:
- 选择 \(x\) 中连续的长为 \(A\) 的子串,将它们全部设为 \(0\)。
- 选择 \(x\) 中连续的长为 \(B\) 的子串,将它们全部设为 \(1\)。
请计算操作结束后的可能达成的不同的 \(x\) 的数量,对 \(10^9+7\) 取模。
题解
考虑对于一个最终串,有没有办法变成全 \(0\)。
首先能够想到两个结论:
- 可以将长度 \(\ge A\) 的串全变成 \(0\)。
- 可以将长度 \(\ge B\) 的串全变成 \(1\)。
那么如果能够将整个串都变成 \(1\),那么就一定能够变成全 \(0\)。
然后你发现这个时候 \(0,1\) 等价了,你的目标是将一个串变成全相同即可。
然后不妨设 \(A\ge B\)(否则同时交换 \(A,B\) 和 \(0,1\))。
然后先给出一个序列能够变成全相同的充要条件:存在一个长度 \(\ge A\) 的子串,其中不包含 \(<B\) 的连续 \(1\) 串。
尝试证明:
必要性:最后一次填写 \(1\) 串的时候,串长度一定 \(\ge B\)。
充分性:先尝试将这个子串外面的随便搞,然后最后一步填到这个子串,然后再填一步 \(1\)。
于是你就可以快乐DP了。
具体的,设 \(f_{i,j,0/1}\) 表示到 \(i\) 为止,已经有长度为 \(j\) 的、不包含 \(<B\) 的 \(1\) 的段,目前最后一位颜色是 \(0/1\)。
然后前缀和优化一下就可以 \(O(n^2)\) 了。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
bool stmemory;
namespace Call_me_Eric{
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 5e3 + 10, mod = 1e9 + 7;
int n, a, b;
int f[maxn][maxn][2], sum[maxn][maxn][2];
int qpow(int x,int a){
int res = 1;
while(a){
if(a & 1){res = res * x % mod;}
x = x * x % mod;a >>= 1;
}
return res;
}
int pw[maxn], pre[maxn];
void main(){
n = read(); a = read(); b = read();if(a < b)swap(a, b);
pw[0] = 1;for(int i = 1;i < maxn;i++){pw[i] = pw[i - 1] * 2 % mod;}
pre[0] = f[0][0][0] = f[0][0][1] = sum[0][0][0] = sum[0][0][1] = 1;
int ans = 0;
for(int i = 1;i <= n;i++){
sum[i][0][1] = f[i][0][1] = (pre[i - 1] - (i - b >= 0 ? pre[i - b] : 0) + mod) % mod;
for(int j = 1;j <= i;j++){
if(j >= b){
int val = sum[i - b][j - b][0];
if(j >= a)ans = (ans + val * pw[max(n - i - 1,0ll)] % mod) % mod;
else f[i][j][1] = val;
}
}
for(int j = 1;j <= i;j++){
int val = sum[i - 1][j - 1][1];
if(j >= a)ans = (ans + val * pw[max(n - i - 1,0ll)] % mod) % mod;
else f[i][j][0] = val;
}
pre[i] = pre[i - 1];
for(int j = 1;j <= i;j++){
pre[i] = (pre[i] + f[i][j][0]) % mod;
sum[i][j][0] = (sum[i - 1][j - 1][0] + f[i][j][0]) % mod;
sum[i][j][1] = (sum[i - 1][j - 1][1] + f[i][j][1]) % mod;
}
}
printf("%lld\n",ans);
return;
}
};
bool edmemory;
signed main(){
auto stclock = clock();
Call_me_Eric::main();
auto edclock = clock();
cerr << (&stmemory - &edmemory) / 1024.0 / 1024.0 << " Mib cost.\n";
cerr << (edclock - stclock) * 1.0 / CLOCKS_PER_SEC << " Sec cost.\n";
return 0;
}
T3
题意
给出一个 \((n+2)\times(n+2)\) 的网格图。
定义一个排列 \(P\) 和它的一个函数 \(f(P)\)。
其中 \(f(P)\) 表示,将所有 \((i,P_i)\) 点标记,则 \(f(P)\) 等于所有从 \((0,0)\to (n+1,n+1)\) 且不经过任意一个标记点的方案数。
现在这个排列有些位置没有填好(对应的 \(P_i=-1\)),问所有可能填好的排列情况的 \(f(P')\) 的总和是多少。
题解
首先先想一个问题,如果给出一个填好的排列,怎么算 \(f(P)\)。
这里给出一个trick,利用容斥来计数。
具体而言,让一条路径的贡献是 \((-1)^{经过标记点的数量}\),证明显然。
然后对于填好的序列,设 \(f_{i,j}\) 表示到点 \((i,j)\) 的方案数,则 \((-1)^{lim_{i,j}}\times f_{i,j}\to f_{i+1,j}\) 和 \(f_{i,j+1}\)。
那对于没填好的序列呢,怎么办?
统计下未确定的点的总数为 \(m\)。
设 \(f_{i,j,k,a,b}\) 表示到点 \((i,j)\),已经钦定了 \(k\) 个未确定的标记点的位置,且当前状态下行 \(i\) (\(a=0\Leftrightarrow\) 没有标记点 / \(a=1\Leftrightarrow\) 有标记点),列 \(j\) (\(b=0\Leftrightarrow\) 没有标记点 / \(b=1\Leftrightarrow\) 有标记点)的方案数。
显然有
当 \(a=0,b=0\) 时
最后答案
没有然后了。
浇浇计数题QWQ
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x= 0, f= 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 210, mod = 998244353;
int f[maxn][maxn][maxn][2][2];
int n, a[maxn], m,fac[maxn];
bool lim[maxn][maxn],line[maxn],row[maxn];
signed main(){
fac[0] = 1; n = read();
for(int i = 1;i <= n;i++)a[i] = read();
for(int i = 1;i <= n;i++){
fac[i] = fac[i - 1] * i % mod;
if(a[i] != -1){
m++; lim[i][a[i]] = line[a[i]] = row[i] = 1;
}
}
m = n - m;int *x;
f[0][0][0][1][1] = 1;
for(int i = 0;i <= n + 1;i++)for(int j = 0;j <= n + 1;j++)
for(int k = 0;k <= m;k++)
for(int a = 0;a < 2;a++)for(int b = 0;b < 2;b++){
int t = f[i][j][k][a][b];if(!t || lim[i][j])continue;
if(!lim[i + 1][j]){
x = &f[i + 1][j][k][row[i + 1]][b];
*x += t;if(*x > mod)*x -= mod;
}
if(!lim[i][j + 1]){
x = &f[i][j + 1][k][a][line[j + 1]];
*x += t;if(*x > mod)*x -= mod;
}
if(a || b)continue;
if(!lim[i + 1][j]){
x = &f[i + 1][j][k + 1][row[i + 1]][1];
*x -= t;if(*x < 0)*x += mod;
}
if(!lim[i][j + 1]){
x = &f[i][j + 1][k + 1][1][line[j + 1]];
*x -= t;if(*x < 0)*x += mod;
}
}
int ans = 0;
for(int i = 0;i <= m;i++){ans = (ans + f[n + 1][n + 1][i][0][0] * fac[m - i]) % mod;}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号