比赛过程
题解
B
题意
给出一个 n * n 的 01 矩阵,求满秩的概率
解法
这道题比较遗憾,是我们队当时花了较多时间去想但是没有想出来,很大的问题还是审题不清,比赛快结束了才看到“Note that when adding up two vectors, the components are modulo 2”这句话,还有就是最后求的是f1 xor f2…xor fn,之前做到过异或的题,然后就老是觉得这个异或应该能够结合公式化简,然而看了题解发现想多了,人家就是单纯的预处理。
这道题首先是推波公式
- 对于第一个向量来说,只需要至少有一个位置为 1 即可,即全部为 0 的情况不满足条件,此时满足条件的情况为2^n-1
- 对于第二个向量来说,需要满足的条件是,不能全部为 0 ,且不能被第一个向量所表示,此时满足条件的情况为2^n-2
- 对于第三个向量来说,同样不能为 0,不能被第一个向量所表示,不能被第二个向量所表示,不能被第一个向量和第二个向量所表示,此时满足条件的情况为2^n-4
- 对于第四个向量来说,不能被前面向量表示的集合为 { ∅ , { 1 } , { 2 } , { 3 }, { 1 , 2 } , {1 , 3 } , { 2 , 3 } , { 1 , 2 , 3 } } ,共八种情况,所以此时满足条件的情况为2^n-8
- …
- 对于第 n 个向量来说,不能被前面 n - 1 个向量单独或混合表示,此时满足条件的情况为2n-2{n-1}
至于为什么不能被两个向量所表示要-1,是因为上面提到的那句话,相当于把向量的加减运算变成异或运算。
最后可以推出:
代码
//头文件太长就不放了
ll Pow(ll a, ll b, ll mode)
{
ll sum = 1;
a = a % mode;
while (b > 0)
{
if (b % 2 == 1) //判断是否是奇数,是奇数的话将多出来的数事先乘如sum
sum = (sum * a) % mode;
b >>= 1;
a = (a * a) % mode; // 不断的两两合并再取模,减小a和b的规模
}
return sum;
}
ll inv(ll x)
{
return Pow(x,mode-2,mode);
}
ll ans[MAXN];
int main()
{
ll t;
read(t);
ll iv2=inv(2);
ll f=ans[1]=iv2;
ll tem=2;
ll iv=iv2;
re(i,2,2e7)
{
tem=tem*2%mode;
iv=iv*iv2%mode;
f=f*(tem-1ll+mode)%mode*iv%mode;
ans[i]=ans[i-1]^f;
}
while(t--)
{
ll n;
read(n);
printf("%lld\n",ans[n]);
}
}
C
题意
一个矩阵的底面积定义为最后一行的数的和,重量定义为所有数的和,给一个正整数矩阵,找一个“压强”最大的可非连续子矩阵。
解法
哈哈哈终于可以说这题不难了,仔细一想不难一定要选择单独的一列,应为如果选择两列,把他拆成两列单独算压强,一定会有一个比两者合起来的压强大,所以就是枚举一波底面即可。
代码
//code已去头
ll mp[205][205];
ll sum[205][205];
int main()
{
int t;
read(t);
while(t--)
{
int n,m;
read(n);
read(m);
ms(mp,0);
ms(sum,0);
re(i,1,n)
re(j,1,m)
{
read(mp[i][j]);
sum[i][j]=sum[i-1][j]+mp[i][j];
}
double ans=0.0;
re(i,1,n)
{
re(j,1,m)
{
double tem=(double)sum[i][j]/(double)mp[i][j];
ans=max(ans,tem);
}
}
printf("%.10lf\n",ans);
}
}
E
题意
给定 n,k,问是否可以构造一个 1~n 的排列 P,使得对于 1~n 中任意的数 i,P 都存在一个长为 i 的子区间,其和模 n 余 k。有解输出任意一组,无解输出 -1。
解法
根据n的奇偶性构造即可
代码
int main()
{
int n,k;
read(n);
read(k);
ll sum=(1+n)*n/2;
sum%=n;
if(sum!=k)
{
printf("-1\n");
return 0;
}
if(n%2)
{
re(i,1,n/2)
{
printf("%d %d ",i,n-i);
}
printf("%d\n",n);
}
else
{
re(i,1,n/2-1)
{
printf("%d %d ",i,n-i);
}
printf("%d %d\n",n/2,n);
}
}
K
题意
• K-bag序列定义为由多个1-k的排列顺序连接起来的序列
• 想问你给定序列是不是k-bag的连续子序列
解法
比赛时曹老师摸到的这道题,摸出来感觉很难,后来确实也没什么想法,看到人家用滑窗加类似dp的思想过了有点吃惊,滑窗的操作之前有遇到过,但这次没想到,趁这次加深映像。
先判断串中有没有大于K的数,有的化直接就-1;然后从1开始维护一个长度为K的滑窗,每次滑动后求滑窗内的数字总类,如果等于K或者等于当前的下标i(由于我们已经排除了包含大于K数字的串,因此这种情况只会出现在
i
<
=
K
i<=K
i<=K的情况下,这是为了处理串前端存在部分
K
−
p
e
r
m
u
t
a
t
i
o
n
K -permutation
K−permutation的情况)如果这时
i
<
=
K
i<=K
i<=K我们就将当前点的状态
c
a
n
[
i
]
can[i]
can[i] 置为可行,就是表示从1到这个点可以构成
K
−
b
a
g
K-bag
K−bag,如果
i
>
K
i>K
i>K就另
c
a
n
[
i
]
=
c
a
n
[
i
−
k
]
can[i]=can[i-k]
can[i]=can[i−k],表示
i
−
k
i-k
i−k 到
i
i
i 这一段是一个
K
−
p
e
r
m
u
t
a
t
i
o
n
K -permutation
K−permutation,1到
i
i
i 能否构成
K
−
b
a
g
K-bag
K−bag取决于
c
a
n
[
i
−
k
]
can[i-k]
can[i−k]。最后倒着从n往下遍历,最多遍历K长度,维护当前遍历到的种类,如果能够遍历到一个点
c
a
n
[
i
]
=
t
r
u
e
can[i]=true
can[i]=true并且当前点的种类等于当前遍历的长度,那么整个序列就可以构成
K
−
b
a
g
K-bag
K−bag,反之不行。最后这一步主要是为了处理后端存在部分
K
−
p
e
r
m
u
t
a
t
i
o
n
K -permutation
K−permutation的情况。
代码
//还有这题又是一道卡map的题
unordered_map<ll, int> mp;
ll a[MAXN];
bool can[MAXN];
int main()
{
ll t;
read(t);
while (t--)
{
ll n, k;
read(n);
read(k);
mp.clear();
ms(can, 0);
bool flag2 = 0;
re(i, 1, n)
{
read(a[i]);
if (a[i] > k)
{
flag2 = 1;
}
}
if (flag2)
{printf("NO\n");continue;}
ll kind = 0;
re(i, 1, n)
{
if (i > k)
{
mp[a[i - k]]--;
if (mp[a[i - k]] == 0)
kind--;
}
if (!mp[a[i]])
kind++;
mp[a[i]]++;
if (kind == k || kind == i)
{
if (i > k)
can[i] = can[i - k];
else
can[i] = 1;
}
}
mp.clear();
kind = 0;
bool flag = 0;
de(i, n, max(1ll, n - k))
{
if (can[i] && kind == n - i)
{
flag = 1;
break;
}
if (!mp[a[i]])
kind++;
mp[a[i]]++;
}
if (flag == 0)
printf("NO\n");
else
printf("YES\n");
}
}