勉勉强强过了三道题,差距很大,继续努力
A 小沙的炉石
原题传送门
可以二分也可以思考后打表过
官方题解:
二分方式:借鉴一个大佬的代码,主要理解这题的思路,见代码解释
#include<bits/stdc++.h>
using namespace std;
#define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl; }
template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
#define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
#define per(i,j,k) for(int i=int(j);i>=int(k);i--)
typedef long long ll;
ll n, m, q, x;
bool check(ll c) {
return c * (m + 1 + m + c) / 2 >= x;
}
int main(){
cin >> n >> m >> q;//读入数据
n = min(n, m + 1);
//找出实际能用的攻击魔法牌数,因为初始体力只有一点,所以最多使用m+1张攻击魔法牌
//如果n大于这个值,则n张牌不能用完,如果n小于这个值,则n张牌可以用完
ll mx = n * (m + 1 + m + n) / 2;//根据等差数列求和求出的能对弟弟造成的最大伤害
//最大伤害序列:m+1,m+2....m+n等差数列求和得上式
//最小伤害序列:1,3,5....2*n-1 等差数列求和得伤害值下限:n^2
//调整任意一张伤害牌和法力牌的出牌顺序,即可让伤害值+-1,所以在伤害上下限区间内的伤害值都是可以达到的
while(q--){//读入数据
scanf("%lld", &x);
if(x <= mx) {//如果最大伤害大于弟弟的生命值,代表有可能刚好斩杀弟弟
ll l = 1, r = n;//二分用的牌数
//已确定最大伤害值大于弟弟的生命值,所以现在要二分牌数(因为不一定要把所有牌都使出)
//
while(l < r) {
int mid = l + r >> 1;
if(check(mid)) r = mid;//检查这个数量的牌能否斩杀弟弟
//因为伤害区间内的任一数都是可以达到的,所以只要牌数mid对应的伤害值大于等于弟弟的生命值,就将牌数左移,直至最后找到刚好斩杀弟弟的生命值
else l = mid + 1;
}
ll mi = l * l ;
//用应出的牌数的平方和弟弟的生命值对比,弟弟的生命值需要大于等于牌数的平方,这一步十分不理解
if(x >= l * l) {
puts("YES");
} else puts("NO");
}
else puts("NO");//如果最大伤害都不大于弟弟的生命值,那就不可能斩杀弟弟
}
}
思考+打表:出题人沙佬的解题思路,上面官方题解讲的就是这种思路,我也是理解二分的方法之后突然理解了这种方法,之前看着代码百思不得其解,只能说这题出的妙
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m, k;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>k;
ll u = min(n, m + 1);//实际能出的牌数
ll r = (m + 1 + m + u) * u / 2;//最大伤害值
while(k -- ){
ll x;
cin>>x;
if(m == 1 && n > 1 && (x == 3 || x > 5)){//特殊情况,打表得出
cout<<"NO"<<endl;
continue;
}
if(m == 2 && x == 8){//特殊情况,打表得出
cout<<"NO"<<endl;
continue;
}
if(r >= x) cout<<"YES"<<endl;//不超过伤害值上限的生命值都可以达到斩杀
else cout<<"NO"<<endl;
}
return 0;
}
B 小沙的魔法
原题传送门
大佬题解:
题解原地址
大佬题解2:
题解2原地址
自己的一部分理解手稿:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
typedef long long ll;
const int N = 5e5 + 10;
struct Node{
int val, id;
}a[N];
ll res;
int n, m;
int fa[N];
vector<int>g[N];
int q[N];
int find(int x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool cmp(Node a, Node b){
return a.val > b.val;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i = 1; i <= n; ++ i) cin>>a[i].val;
for(int i = 1; i <= n; ++ i){
a[i].id = i;
fa[i] = i;
res += a[i].val;
q[i] = a[i].val;
}
//res记录朴素操作所有点的权值之和
//id记录对应下标
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= m; ++ i){
int r1, r2;
cin>>r1>>r2;
//将所有连通的城市加入一个集合中
g[r1].pb(r2);
g[r2].pb(r1);
}
for(int i = 1; i <= n; ++ i){
for(auto x : g[a[i].id]){//遍历所有和第i个点联通的点
int u = find(a[i].id), v = find(x);
if(q[u] > q[v] || u == v) continue;
res = res - q[u] - q[v] + max(q[u], q[v]);
q[u] = q[v] = min(q[u], q[v]);
fa[u] = v;
}
}
cout<<res<<endl;
return 0;
}
D 小沙的涂色
这题的涂色思路要好好借鉴
大佬解题思路:大模拟构造题。根据 n % 3 的值分别为 0,1,2 时进行构造(构造方法很多,下面代码用的其中一种)。注意 n % 3 = 0 时无法满足题意,除此之外还需特判 n == 1 时的情况。
大佬题解
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
typedef long long ll;
const int N = 1e3 + 10;
int n;
int idx;
int a[N][N];
void draw1(int x, int y){
int i = x;
for(int j = y; j <= n; j += 3){
a[i][j] = a[i][j + 1] = a[i + 1][j] = ++ idx;
a[i][j + 2] = a[i + 1][j + 1] = a[i + 1][j + 2] = ++ idx;
}
}
void draw2(int x){
for(int i = x; i <= n; ++ i){
if(i & 1) a[i - 1][1] = a[i][1] = a[i][2] = ++ idx;
else a[i - 1][3] = a[i][2] = a[i][3] = ++ idx;
}
}
void draw3(int x, int y){
for(int i = x; i <= n; i += 3){
for(int j = y; j <= n; j += 2){
a[i][j] = a[i][j + 1] = a[i + 1][j] = ++ idx;
a[i + 1][j + 1] = a[i + 2][j] = a[i + 2][j + 1] = ++ idx;
}
}
}
void Put(){
cout<<"YES"<<endl;
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= n; ++ j){
cout<<a[i][j]<<" ";
}
cout<<endl;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
if(n % 3 == 0) cout<<"NO"<<endl;
else if(n == 1) cout<<"YES"<<" "<<0<<endl;
else if(n % 3 == 1){
a[1][1] = a[1][2] = a[2][1] = 1;
a[4][4] = a[4][3] = a[3][4] = 2;
a[3][1] = a[3][2] = a[4][2] = 3;
a[2][2] = a[2][3] = a[3][3] = 4;
a[1][4] = a[1][3] = a[2][4] = 5;
idx = 5;
draw1(1, 5);
draw1(3, 5);
if(n & 1) draw2(5), draw3(5, 4);
else draw3(5, 1);
Put();
}
else if(n % 3 == 2){
a[1][1] = a[1][2] = a[2][2] = ++ idx;
draw1(1, 3);
if(n & 1) draw2(3), draw3(3, 4);
else draw3(3, 1);
Put();
}
return 0;
}
E 小沙的长路
原题传送门
对于最长路最短的情况:只需要构成一个无环图即可,即最短的最长距离为n-1
对于最长路的情况,就需要走遍每个边,这就需要知道欧拉路径的概念(大佬讲的概念),然后就会发现最大最长距离其实就是欧拉路径,之后针对点的度数分别为偶数/奇数区别运算即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
int n;
cin>>n;
if(n & 1) cout<<n - 1<<" "<<n * (n - 1) / 2<<endl;
else cout<<n - 1<<" "<<n * (n - 1) / 2 - n / 2 + 1<<endl;
return 0;
}
F 小沙的算数
原题传送门
官方题解:
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N = 1e6 + 10, mod = 1000000007;
ll gmi(ll a, ll k){//快速幂
int res = 1;
a %= mod;
while(k){
if(k & 1) res = res * a % mod;
a = a * a % mod;
k >>= 1;
}
return res;
}
ll ni(ll x){//求逆元
return gmi(x, mod - 2) % mod;
}
int n, q;
char str[N];
ll ans;//记录和
ll b[N], a[N], ps[N];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>q;
cin>>str + 1;//记录运算符
str[n] = '+';//设置边界
for(int i = 1; i <= n; ++ i){
cin>>a[i];//计数
}
int pos = 0;//记录位置
int res = 1;//记录每一块的乘积
memset(ps, -1, sizeof ps);
for(int i = 1; i <= n; ++ i){
res = res * a[i] % mod;//以加号为分割点,分隔开各个惩罚块
if(str[i] == '+'){
ps[i] = pos;//记录在第几块内
b[pos ++ ] = res;//存在一个块内
res = 1;
}
else ps[i] = pos;//记录在第几块内
}
for(int i = 0; i < pos; ++ i) ans = (ans + b[i]) % mod;//所有数之和
while(q -- ){//更改
int x, y;
cin>>x>>y;
ll pi = ps[x];//先找到x在那个块内
ll temp = (b[pi] * y % mod * ni(a[x])) % mod;//更改x对应的块
if(temp >= b[pi]) ans = (ans + (temp - b[pi] + mod) % mod) % mod;//如果这一块变大了,那么对应的总和也要变大
else ans = (ans - (b[pi] - temp + mod) % mod + mod) % mod;//变小就变小
a[x] = y;
b[pi] = temp;//将新的这一块的值放入
cout<<ans<<endl;
}
return 0;
}
G 小沙的身法
H 小沙的数数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, mod = 1e9 + 7;
ll n, m;
ll a, b;
string str;
ll ans;
int qmi(ll a, ll k, ll p){//求a的k次幂mod p的值
ll res = 1;//用res记录答案
a = a % mod;
while(k){//当k不为0时就一直循环
if(k & 1) res = ((res % mod) * (a % p)) % mod;//当k二进制的最后一位为1时,表示k分解的二的次方数中有这一位,此时进行运算
k >>= 1;//k右移一位
a = ((a % mod) * (a % p)) % mod;//a进位
}
return res;
}
ll get(ll b){//求b由几位二进制数构成
ll res = 0;
while(b){
if(b & 1) res ++ ;
b = b >> 1;
}
return res;
}
int main()
{
scanf("%lld%lld", &n, &m);
ll ci = get(m);
cout<<qmi(n, ci, mod)<<endl;
return 0;
}