A-校园活动
思路
了解程度的总和才(9000),直接遍历分组数量,从(n)到(1)遍历,一旦能够分组就输出.
因为(0)对分组数量没有影响,所以只存正整数,方便操作.
注意全是(0)的情况答案是(n)
代码
#include <bits/stdc++.h>
using namespace std;
int n, sum;
vector<int> arr;
int check(int num)
{
if (sum % num) return 0;
int tar = sum / num;
int tmp = 0;
for (int x : arr)
{
if (tmp > tar) return 0;
if (tmp == tar) tmp = 0;
tmp += x;
}
return tmp == tar;
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n;++i)
{
char c;
scanf(" %c", &c);
if (c-'0') arr.push_back(c-'0');
sum += c-'0';
}
if (sum == 0)
{
printf("%d
", n);
}else{
int ans = arr.size();
while (ans > 1)
{
if (check(ans)) break;
ans--;
}
if (ans < 2) printf("-1
");
else printf("%d
", ans);
}
}
C-CG的通关秘籍
思路
列出所有情况可以发现一共有(m^n)种.
当他填第(k, kin[1,n))位数字的时候,这样考虑贡献.
任何数字在第(k)位上出现的概率都是(frac{1}{m}).
所以对于每一个数字(p, pin[1,m]), 有(frac{m^n}{m}=m^{n-1})种情况第(k)位是(p).
在这(m^{n-1})种情况中,任何数字在(k+1)位上出现的概率都是(frac{1}{m}).
所以对于每一个数字(q,qin[1,m]),有(frac{m^{n-1}}{m}=m^{n-2})种情况第(k)位是(p)并且第(k+1)位是(q).
考虑每一对可能的(p,q)
(p<q)的情况有(frac{m*(m-1)}{2}*m^{n-2})种,贡献是(frac{m(m-1)}{2}*m^{n-2})
(p>q)的情况有(frac{m*(m-1)}{2}*m^{n-2})种,贡献是(frac{2m(m-1)}{2}*m^{n-2})
所以第(k)位的贡献是(frac{3m(m-1)}{2}*m^{n-2})
最后一位数字的后面没有东西,所以他没有贡献
最终答案是(frac{3m(m-1)}{2}*m^{n-2}*(n-1))
要用快速幂求(m^n-2)和(2)的逆元.
代码
#include <bits/stdc++.h>
using namespace std;
const int p = 1e9+7;
int read()
{
static char c = 0;
int ret = 0;
while (c <'0' || c > '9') c = getchar();
while ('0' <= c && c <= '9')
{
ret = ret * 10 + c -'0';
c = getchar();
}
return ret;
}
int qpow(int a, int x)
{
int ret = 1;
while (x)
{
if (x&1) ret = 1ll * ret * a % p;
a = 1ll * a * a % p;
x >>= 1;
}
//printf("%d
", ret);
return ret;
}
int main()
{
int t = read();
while (t--)
{
int n = read(), m = read();
printf("%lld
", 3ll * (n-1) % p * qpow(m, n-2) % p
* m % p * (m-1) % p * qpow(2, p-2) % p);
}
}
E-牛牛数数
思路
题目要求每个(x)只计算一次,每个(x)都是由说给的数字异或产生的.
给了(10^5)个数字,可以构造一个线性基
线性基有以下特性
- 线性基的元素之间互相异或构造出来的集合,与原本数组的元素之间相互异或构造出来的集合相同,对于本题,这个性质保证不遗漏
- 线性基中选择不同的子集,异或出来的数字都是不同的,对于本题,这个性质保证不重复计算
- 线性基是满足以上性质的最小集合,通常元素数量不大于数字的二进制位数,对于本题,线性基顶多会有(60)个元素
- 线性基各个元素的最高位(1)互不相同
所以我们先求出线性基.
然后用一个操作改变线性基,使得满足以下性质
任意一个元素(x),对于其他所有小于他的元素(y),如果(y)的最高位(1)位于第(p)位,那么(x)的第(p)位一定为(0).
然后从大到小排序.
设置一个变量(v=0).
从大到小遍历线性基的每个元素.
情况1
如果线性基的当前元素异或(v)的值大于(K).因为任意一个元素(x),对于其他所有小于他的元素(y),如果(y)的最高位(1)位于第(p)位,那么(x)的第(p)位一定为(0).
所以之后的值无论如何选择,(v)都会不会变小,所以最终产生的(x)一定大于(K).
那么当取这个元素之后,后面每个元素都可以取或者不取,对答案的贡献就是(2^{还未被遍历的元素的个数}).
但是不要直接把这个元素异或(x),因为取这个元素的方案已经考虑完了,之后(v)都是不带这个元素的.
情况2
如果线性基的当前元素异或(v)的值小于(K).那么不取这一个元素的情况一定不会有贡献.
因为后面的元素的(1)的最高位不会高过这一个元素,所以不取这一个元素的话,后面怎么取叶不会比(K)大.
取了这个元素的情况下有没有贡献我们还不知道,要到考虑后面的元素才能知道,这里先把这个元素异或(v).
代码
#include <bits/stdc++.h>
using namespace std;
long long read()
{
static char c = 0;
long long ret = 0;
while (c <'0' || c > '9') c = getchar();
while ('0' <= c && c <= '9')
{
ret = ret * 10 + c -'0';
c = getchar();
}
return ret;
}
int main()
{
int n = read();
long long K = read();
long long ans = 0, v = 0;
vector<long long> base;
for (int i = 1; i <= n; ++i)
{
long long x = read();
for (long long y : base) x = min(x, x^y);
if (x) base.push_back(x);
}
for(int i = 0; i < base.size(); i++)
for(int j = 0; j < base.size(); j++)
if(i != j)
base[i] = min(base[i], base[i] ^ base[j]);
sort(base.begin(), base.end(), greater<long long>());
//for (long long x : base) printf("%lld
", x);
for (int j = 0; j < base.size(); ++j)
{
if ((v ^ base[j]) > K)
ans += 1ll << (base.size() - j - 1);
else
v ^= base[j];
}
printf("%lld
", ans);
}