EOJ Monthly 2020.3

B. 与矩阵
单点时限: 1.0 sec
内存限制: 512 MB

前有牛顿瘟疫“家里蹲”发明微积分。

现有 Cuber QQ 新冠肺炎“家里蹲”发明与矩阵。
与矩阵是一个 n×n 的矩阵。规定矩阵中的第 i行第 j 列记为 (i,j)。
生成一个与矩阵的方式是,先生成一个长度为 n的数列 a1,a2,…,an−1,an,而矩阵中 (i,j)=ai&aj

其中 & 是指按位与运算,其计算方式是参与运算的两数各对应的二进位相与。只有对应的两个二进位都为 1 时,结果位才为 1。

Cuber QQ 发现,同一个与矩阵可能对应着一些不同的数列,不过 Cuber QQ 现在只想知道字典序最小的数列是什么样的。

对于两个数列 a1,a2,…,an−1,an和 b1,b2,…,bn−1,bn,如果存在一个整数 k(1≤k≤n) 满足 ak+11=b1,a2=b2,…,ak=bk,我们就认为数列 a1,a2,…,an−1,an的字典序要小于数列 b1,b2,…,bn−1,bn

当然,Cuber QQ 不会这么容易让你得到答案,他会把矩阵所有的 (i,i)(1≤i≤n) 的位置全部隐藏,只显示为 0。

输入格式
第一行输入一个整数 n(1≤n≤1000) ,表示矩阵的大小。接下来的 n行,每行 n个用空格隔开的整数 ai,1,ai,2,…,ai,n(0≤ai,j≤107) ,表示与矩阵。
输入保证至少存在一个可能的解。

输出格式
输出包含一行
n个用空格隔开的数,表示字典序最小的数列。

样例
Input
3
0 0 1
0 0 2
1 2 0
Output
1 2 3
提示
样例中给出的数列为 1,2,3。

肯定要按位考虑。对于一个位置 (i,j)考虑二进制下的其中一位,如果它所在的行和列中的数在这一位上出现了 1,那么必然这个数这一位上一定是 1。
题目要求字典序最小的,那么我们必然会把所有可以为 0 的位置全部设为 0,也就是它所在的行和列中的数在这一位上没有出现了 1。
很容易证明,这样的到的一定是最小的。

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1010;
const double eps = 1e-7;
int a[maxn][maxn];
int ans[maxn];
int n;
void change(int x){
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			int y=a[i][j]/x;
			a[i][j]%=x;
			if(y==1){
				ans[i]|=x;
				ans[j]|=x;
			}
		}
	}
}
int main(void){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&a[i][j]);
		}
	}
	for(int i=25;i>=0;i--){
		change(1<<i);
	}
	for(int i=1;i<=n;i++){
        if(i!=1) printf(" ");
		printf("%d",ans[i]);

	}
	return 0;
}

D. 钢琴演奏家
单点时限: 1.5 sec
内存限制: 512 MB
Cuber QQ 在疫情期间已经宅在家两个月了。
实在是无所事事的他,决定重操旧业,继续实现他曾经梦寐的钢琴演奏家梦想。
掀开积满了灰尘的钢琴盖,是他许久都未触碰的琴键,按下的瞬间,他发现,钢琴坏了。
Cuber QQ 有一个多年的弹奏习惯,他弹奏钢琴,同一时刻一定会同时按下 m个琴键,他喜欢不同音调交织在一起的声音,可是现在不允许了。
可能是因为时间的原因,钢琴不支持琴键并行(音乐带师 Cuber QQ 发明的词汇)了。通俗来说,当 Cuber QQ 同时按下 m 个琴键的时候,钢琴只会发出音调最高的那个琴键的声音。
不甘心的 Cuber QQ 开始尝试每一个 m 键的组合。他会记录下每一次钢琴发出的音调,他会统计所有演奏出的音调之和,为了验证自己有没有算错,他邀请你来帮他再算一遍。
需要注意的是,因为钢琴坏了,所以可能存在相同音调的琴键。
由于这个和可能会很大,你只需要告诉 Cuber QQ 这个和模 109+7
的结果是多少。
输入格式
输入数据第一行包含一个整数 T(1≤T≤1000) 表示数据组数。
对于每一组数据,第一行包含两个整数 n, m(1≤m≤n≤106) ,分表表示钢琴的琴键数量和每次同时按下的琴键个数。
第二行包含 n个整数 a1,a2,…,an(0≤ai≤109),表示琴键的音调(可能会出现相同的音调)。
保证对于所有数据有 ∑n≤106
输出格式
对于每组数据输出一行,包含一个整数表示答案。
由于答案可能很大,需要对 109+7取模。
样例
Input
1
3 2
1 2 3
Output
8

考虑每一个数作为最大数的时候的情况。
如果将给定的值排序以后,每一个数产生贡献的时候,所选取的其他值一定都在他的一侧,可以直接通过组合公式计算出当前值作为最大值时候的贡献。
于是可以得到答案为
∑ i = m n a i ∗ C ( m − 1 , n − 1 ) \sum_{i=m}^na_i*C(m-1,n-1) i=mnaiC(m1,n1)
而对于可能出现相同值的情况,我们可以在排序以后默认左边的数要大于右边(即当我们按下的琴键中有多个最大值的时候,我们认为位置最左边的最小,显然这样处理可以不重不漏)。

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 2000010;
const ll mod = 1e9+7;
ll num[maxn];
ll fac[maxn],inv[maxn];
ll pow_mod(ll a,ll n)
{
    ll ret =1;
    while(n)
    {
        if(n&1) ret=ret*a%mod;
          a=a*a%mod;
          n>>=1;
    }
    return ret;
}
void init()
{
    fac[0]=1;
    for(int i=1;i<maxn;i++)
    {
        fac[i]=fac[i-1]*i%mod;
    }
}
ll Cc(ll x, ll y)
{
    return fac[x]*pow_mod(fac[y]*fac[x-y]%mod,mod-2)%mod;
}
int main(void)
{
    int T;
    int n,m;
    init();
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%lld",&num[i]);
        sort(num+1,num+n+1);
        ll sum = 0;
        for(int i=n;i>=m;i--){
            sum = (sum + Cc(i-1,m-1)*num[i]%mod)%mod;
        }
        printf("%lld\n",sum);
    }

    return 0;
}

你可能感兴趣的:(#,EOJ)