题解只供参考,哪里有问题欢迎提出来
n比较大,卡你时间,采用构造矩阵,再用矩阵快速幂解决,不过这样还是有点问题,因为mod值比较大,还需考虑一个快速乘,这题最难的点在于如何构造矩阵,在此贴上一篇博客
https://blog.csdn.net/Akatsuki__Itachi/article/details/80443939
#include
using namespace std;
typedef long long ll;
typedef struct {
ll a[4][4];
}matrix;
const ll mod = 1e10+19;
ll add(ll a, ll b, ll c){ //快速乘
ll r = 0;
while(b){
if(b & 1) r = (r+a)%c;
a = (a*2)%c;
b /= 2;
}
return r;
}
matrix mul(matrix p, matrix q){ //4阶矩阵乘法
matrix d;
for(ll i = 0; i < 4; i++){
for(ll j = 0; j < 4; j++){
d.a[i][j] = 0;
for(ll k = 0; k < 4; k++){
d.a[i][j] += add(p.a[i][k],q.a[k][j],mod); //考虑mod值过大采用快速乘
d.a[i][j] %= mod;
}
}
}
return d;
}
matrix kmi(matrix p, ll num){ //矩阵快速幂
matrix res;
memset(res.a,0,sizeof(res.a));
for(int i = 0; i < 4; i++){ //构造单位矩阵
res.a[i][i] = 1;
}
while(num){
if(num & 1) res = mul(res,p);
num /= 2;
p = mul(p,p);
}
return res;
}
int main(){
ll n;
cin >> n;
matrix b, c;
memset(b.a,0,sizeof(b.a));
memset(c.a,0,sizeof(c.a));
b.a[0][1] = 1;
b.a[1][0] = 1;
b.a[1][1] = 1;
b.a[2][1] = 1;
b.a[2][2] = 1;
b.a[3][1] = 1;
b.a[3][2] = 1;
b.a[3][3] = 1;
c.a[0][0] = 1;
c.a[0][1] = 1;
c.a[0][2] = 3;
c.a[0][3] = 1;
b = kmi(b,n-1);
c = mul(c,b);
cout << c.a[0][0] << endl;
return 0;
}
这个题目考的数学里面的容斥原理
两个集合的容斥关系公式:A∪B = A+B - A∩B (∩:重合的部分)
三个集合的容斥关系公式:A∪B∪C = A+B+C - A∩B - B∩C - C∩A +A∩B∩C
四个有限集合 :A∪B∪C∪D=A+B+C+D- A∩B - B∩C - C∩A- A∩D - B∩D -C∩D+A∩B∩C+A∩B∩D +A∩C∩D +B∩C∩D -A∩B∩C∩D
#include
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
ll n, a, b, c, d, sum;
cin >> n;
cin >> a >> b >> c >> d;
sum = n/a + n/b + n/c + n/d - n/a/b - n/a/c - n/a/d - n/b/c - n/b/d - n/c/d - n/a/b/c/d + n/a/b/c + n/a/b/d+ n/a/c/d+n/b/c/d;
cout << sum << endl;
return 0;
}
#include
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
ll n, sum;
cin >> n;
sum = 1;
for(int i = 1; i < n; i++){
sum *= 2;
}
cout << sum << endl;
return 0;
}
一般求a^b的话应该采用快速幂的方法,时间更快,有更优的方法肯定采用优的方法,我建议用这种方法去做
#include
using namespace std;
typedef long long ll;
ll power(ll a, ll b){
ll ans = 1;
while(b){
if(b & 1) ans *= b;
b /= 2;
a *= a;
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
ll n;
cin >> n;
cout << pow(2,n-1) << endl;
return 0;
}
此题是一个部分背包问题,题目意思很简单,算出每种物品的性价比,然后从大到小排序,直到钱用完为止。
#include
using namespace std;
struct node{
int v,w;
double c;
}a[1005];
bool cmp(node a, node b){
if(a.c > b.c) return true;
return false;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for(int i = 0; i < n; i++){
cin >> a[i].w;
}
for(int i = 0; i < n; i++){
cin >> a[i].v;
a[i].c = 1.0*a[i].v/a[i].w; //算出每种物品的性价比
}
sort(a,a+n,cmp);//从大到小排序
double sum = 0;
int i = 0;
while(m > 0){
if(m >= a[i].w) m-=a[i].w,sum+=a[i].v;
else if(m > 0) sum += a[i].c*m,m=0;
i++;
}
sum *= 10;
cout << fixed << setprecision(1) << sum << endl;
return 0;
}
由于题目数据的原因,重新改了一下
这题就是一个一元二次方程解答而已,但要考虑脚只能是偶数,猪和牛不能为负就行,超级简单。
#include
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, m, a, b;
cin >> m >> n >> a >> b;
int flag = 0;
if((m-2*n) % (2*b) == 0) {
int y = (m-2*n)/(2*b);
int x = (n-b*y)/a;
if(x >= 0 && y >= 0){
cout << x <<" "<< y << endl;
flag = 1;
}
}
if(flag == 0) cout << -1 << endl;
return 0;
}
题目的意思就是找出1个或者2个数来。
这就要用到位运算的异或啦,找一个数很简单,相同的异或就为0,剩下的一个值就是要求的答案了。主要讲讲怎样求2个数
首先将所有元素异或得到那两个出现一次的数的异或值,然后寻找这个值的二进制中第一个为1的位置,然后再用它与每个元素按位与,将所有元素进行分组,这时那两个出现一次的数一定在不同组里,而且相同的数一定在同一组里,所以此时再让两个数组都分别进行异或,这样就得到了那两个所求值。
#include
using namespace std;
typedef long long ll;
const int maxn = 3000000+9;
int a[maxn];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, k, res;
cin >> n >> k;
res = 0;
for(int i = 0; i < n; i++){
cin >> a[i];
res ^= a[i];
}
if(k == 1){
cout << res << endl;
}
else {
int x, y, flag;
flag = 1;
x = 0, y = 0;
for(int i = 0; i < 32; i++){ //找到1出现的位置
if(res & (flag<<=i)){
break;
}
}
for(int i = 0; i < n; i++){
if(flag & a[i]){ //进行分组
x ^= a[i];
}else{
y ^= a[i];
}
}
if(x > y) swap(x,y);
cout << x << " " << y << endl;
}
return 0;
}
n&-1 = n;//输出n即可
时间戳用处
对于询问 x 是不是 y 的祖先,我们只要判断一下时间戳区间的包含关系
做这题要用到一个叫时间戳的东东,运用第一次访问结点和第二次访问的结点进行判断。知道这个就简单了,用深度优先搜索dfs就可以解决啦
#include
using namespace std;
const int maxn = 1e5+9;
vector<int>e[maxn];
int in[maxn],out[maxn];//第一次访问的时间,第二次访问时间
int cnt;
void dfs(int u, int fa){
in[u] = ++cnt;
for(int i = 0; i < e[u].size(); i++){
if(e[u][i] == fa) { //访问到叶子结点进行下另一个分支
continue;
}
dfs(e[u][i],u);
}
out[u] = cnt;
return;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, m, k, u, v;
cin >> n >> m >> k;
for(int i = 1; i < n; i++){
cin >> u >> v;
e[u].push_back(v);//采用向量来存储2个结点的关系
e[v].push_back(u);
}
cnt = 0;
dfs(m,-1);
while(k--){
cin >> u >> v;
if(in[u] <= in[v] && out[u] >= out[v]){
cout << 1 << endl;
}else if(in[u] >= in[v] && out[u] <= out[v]){
cout << 2 << endl;
}else{
cout << 3 << endl;
}
}
return 0;
}
上一题求了树,其实两题差不多,不过后面一题更简单,直接在根节点来一遍搜索记住每一个结点与子结点的权值就行。`
#include
using namespace std;
const int maxn = 1e5+9;
vector<int>e[maxn];
int w[maxn];
void dfs(int u, int fa){
for(int i = 0; i < e[u].size(); i++){
if(e[u][i] == fa) { //访问到叶子结点进行下另一个分支
continue;
}
// w[u] += w[e[u][i]]; //先加会导致错误,自己理解理解
dfs(e[u][i],u); //访问子结点
w[u] += w[e[u][i]]; //访问完子结点然后再加上子结点的值,由叶子节点往上走。
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, m, k, u, v;
cin >> n >> m >> k;
for(int i = 1; i <= n; i++){
cin >> w[i];
}
for(int i = 1; i < n; i++){
cin >> u >> v;
e[u].push_back(v);//采用向量来存储2个结点的关系
e[v].push_back(u);
}
dfs(m,-1);
while(k--){
cin >> u;
cout << w[u] << endl;
}
return 0;
}
这题本人卡了好多遍,题目意思好简单,但有一个坑,特别容易入坑,首先要判断第一个数是否为大小写,但你要考虑全部为空格的,直接换行,友情提醒(不能输空格,只能是换行)
#include
using namespace std;
int main(){
string str;
int n;
cin >> n;
getchar();
while(n--){
getline(cin,str);
int i = 0;
for(; i < str.size(); i++){ //找出第一个不是空格的字符
if(str[i] != ' ') break;
}
if(i >= str.size()) { //全部是空格,直接换行
cout << endl;
continue;
}
int flag = 0; //记录第一个字符是大写还是小写
if(str[i] >= 'A' && str[i] <= 'Z'){
flag = 1;
}
cout << str[i]; //输出第一个字符
i++;
for(; i < str.size(); i++){
if(str[i] == ' ') continue;
if(flag == 0) { //当第一个字符为小写
if(str[i-1] == ' ' && str[i] >= 'a' && str[i] <= 'z'){
cout << char(str[i]-32);
continue;
}
}
else{//当第一个字符为大写
if(str[i] >= 'A' && str[i] <= 'Z' && str[i-1] == ' ') {
cout << char(str[i]+32);
continue;
}
}
cout << str[i];
}
cout << endl;
}
return 0;
}
这题有个坑点就是q的值可以是负数。这题求个数的数据不大,用桶排序就ok了,由于有负数,本人考虑用一个二维数组就好。
#include
using namespace std;
const int maxn = 100000+9;
int num[maxn][2];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, m, k;
cin >> n >> m;
memset(num,0,sizeof(num));
for(int i = 0; i < n; i++){
cin >> k;
if(k < 0) num[abs(k)][0]++;
else num[k][1]++;
}
while(m--){
cin >> k;
if(k < 0)
cout << num[abs(k)][0] << endl;
else
cout << num[k][1] << endl;
}
return 0;
}
其实这题很简单,咱们变个型
x1/x2/x3/x4 = x1x3x4/x2
因为x2无论如何加括号,x2只能当分母,而其他数全部当分子啦,所以这题就转化成求分子分母的公约数啦,当分母为1,这个结果就是整数啦
#include
using namespace std;
const int maxn = 10000+9;
int a[maxn];
int gcd(int a, int b){
return b == 0 ? a : gcd(b,a%b);
}
bool judge(int m){
a[1] /= gcd(a[0],a[1]);
if(a[1] == 1) return true;
for(int i = 2; i < m; i++){
a[1] /= gcd(a[i],a[1]);
}
return a[1] == 1;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, m, flag;
cin >> n;
while(n--){
flag = 0;
cin >> m;
if(m == 1) flag = 1; //如果为一个数,那肯定是整数啦
for(int i = 0; i < m; i++){
cin >> a[i];
}
if(flag == 1) {
cout << "Yes" << endl;
continue;
}
if(judge(m)){
cout << "Yes" << endl;
}
else{
cout << "No" << endl;
}
}
return 0;
}
这题不要被线段树吓到了(也可能有些人不知道啥是线段树,不知道还好,刚开始看这题我被吓到了)这题就是按步骤一步步来就行,需要注意一下靠前的那个条件就行啦,简单,直接上代码
#include
using namespace std;
int a[1001];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
while(m--){
int l, r,x,y;
cin >> x >> y;
int maxx = -1;
int minn = 1009;
for(int i = x; i <= y; i++){
if(maxx < a[i]){
maxx = a[i];
l = i;
}
if(minn > a[i]){
minn = a[i];
r = i;
}
}
swap(a[l],a[r]);
}
for(int i = 1; i < n; i++){
cout << a[i] << " ";
}
cout << a[n] << endl;
return 0;
}