#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
map<int, int>mp;
int main(void) {
int n, c, x;
cin >> n >> c;
for (int i = 1; i <= n; ++i) {
cin >> x;
mp[x]++;
}
int ans = 0;
for (auto t : mp) {
ans = ans + t.second * mp[t.first - c];
}
cout << ans;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
typedef long long ll;
const int mod = 998244353;
ll qmul(ll x, ll y) { //返回x*y模mod后的值。转化成二进制加法(快速加)
ll ret = 0;
while (y) {
if (y & 1) //二进制下,若y末尾为1
ret = (ret + x) % mod; //则加x*1,并将x左移1位。
x = (x << 1) % mod; //若y末尾为0,则加x*0,并将x左移1位,即直接左移。
y = y >> 1; //准备计算y前一位的值
}
return ret;
}
int main(void) {
ll n,i;
int t = 10;
cin >> n;
if (n < 10) {
cout << n;
return 0;
}
ll ans = 0;
for (i = 10; i <= n; i = i * 10) {
//ans = (ans + ((1 + i - i / 10) % mod) * ((i - i / 10) % mod) / 2) % mod;
if ((1 + i - i / 10) % 2 == 0)
ans = ans + qmul((1 + i - i / 10) / 2, i - i / 10);
else ans = ans + qmul(1 + i - i / 10, (i - i / 10) / 2);
}
//ans = ans + ((1 + (n - i / 10 + 1))%mod) * ((n - i / 10 + 1)%mod) / 2;
if((1 + n - i / 10+1)%2==0)
ans = ans + qmul((1 + n - i / 10+1) / 2, n - i / 10 + 1);
else ans = ans + qmul(1 + n - i / 10+1 , (n - i / 10 + 1)/2);
cout << ans%mod<<"\n";
return 0;
}
两个大数取模,每个先取模,再相乘后取模,应该也是可以的,写成上面注释掉了的代码也ac了
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
const int mod = 1e9+7;
const int N = 1e6 + 10;
int dp[N][2];//代表第i位是0或1的个数
int n, k;
signed main(void) {
cin >> n >> k;
dp[0][0] = 1;
//dp[0][1] = 1;
for (int i = 1; i <= n; ++i) {
dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
dp[i][1] = (dp[i - k - 1 > 0 ? i-k-1 : 0][0] + dp[i - k - 1 > 0 ? i-k-1 : 0][1]) % mod;
}
cout << (dp[n][0] + dp[n][1]) % mod;
return 0;
}
假设第i位要填0,则前一位是0或1都没有什么影响
假设第i位要填1,则至少第i-k位到第i-1位都要是0,但是如果第i-k-1位是0就没有影响了
所以 d p [ i ] [ 1 ] = d p [ i − k − 1 ] [ 0 ] + d p [ i − k − 1 ] [ 1 ] dp[i][1]=dp[i-k-1][0]+dp[i-k-1][1] dp[i][1]=dp[i−k−1][0]+dp[i−k−1][1] 为什么啊,因为后面这几位都要是0
初始状态设 d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1 (为什么啊,为什么 d p [ 0 ] [ 1 ] dp[0][1] dp[0][1]不也设成1,举个例子就知道了)
然后考虑当i-k-1小于0时,就默认从 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]转移过来 why???
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
using namespace std;
queue<int>q;
const int mod = 1e9+7;
const int N = 1e6 + 10;
int dis[N];
int ch[N];
void bfs(int v, int u) {
if (v >= 1 && v <= 1e5 && dis[v] == 0x3f3f3f3f) {
dis[v] = dis[u] + 1;
q.push(v);
}
}
signed main(void) {
int a, x;
cin >> a >> x;
memset(dis, 0x3f, sizeof(dis));
dis[a] = 0;
for (int i = 1; i <= x; ++i) cin >> ch[i];
q.push(a);
while (!q.empty()) {
int u = q.front();
bfs(u+1, u);
bfs(u * 2, u);
bfs(u * 3, u);
bfs(u - 1, u);
q.pop();
}
for (int i = 1; i <= x; ++i) {
cout << dis[ch[i]] << " ";
}
return 0;
}
初始值设为最大值用来判断这个数字有没有出现过,如果出现过就没必要继续进行操作了,因为在这之前出现的数的dis肯定更小
第二次自己做:
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
using namespace std;
const int N = 2e6 + 10;
int ch[N];
int vis[N], ans[N];
queue<int>x;
map<int, int>mp;
int main(void) {
int a, q;
cin >> a >> q;
for (int i = 1; i <= q; ++i) {
scanf("%d", &ch[i]);
mp[ch[i]] = 1;
}
int cnt = 0;
x.push(a);
vis[a] = 1;
ans[a] = 0;
if (mp[a] == 1) cnt++;
while (!x.empty()) {
int n = x.front();
x.pop();
if (n > 1e5) continue;
if (n + 1 <= 1e5) {
if (vis[n + 1] == 0) {
x.push(n + 1);
ans[n + 1] = ans[n] + 1;
vis[n + 1] = 1;
if (mp[n + 1] == 1) cnt++;
}
}
if (n * 2 <= 1e5) {
if (vis[n * 2] == 0) {
x.push(n * 2);
ans[n * 2] = ans[n] + 1;
vis[n * 2] = 1;
if (mp[n * 2] == 1) cnt++;
}
}
if (n * 3 <= 1e5) {
if (vis[n * 3] == 0) {
x.push(n * 3);
ans[n * 3] = ans[n] + 1;
vis[n * 3] = 1;
if (mp[n * 3] == 1) cnt++;
}
}
if (n - 1 <= 1e5&&n-1>=0) {//不特判会RE
if (vis[n - 1] == 0) {
x.push(n - 1);
ans[n - 1] = ans[n] + 1;
vis[n - 1] = 1;
if (mp[n - 1] == 1) cnt++;
}
}
if (cnt >= q) break;//如果都出现过了就break呗,但是为什么比上面的要慢啊
}
for (int i = 1; i <= q; ++i) {
printf("%d ", ans[ch[i]]);
}
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
using namespace std;
signed main(void) {
int x;
cin >> x;
int i = 1;
int ans = 1;
int first = 1;
if (x == 1) {
cout << 1 << " " << 1;
return 0;
}
while (1) {
if (i % x != 0) {
i = (i % x) * 10 + 1;
ans++;
if (i / x == 0) {
if (!first) cout << 0;
}
else {
cout << i / x;
first = 0;//不能输出前导0
}
}
else break;
}
cout << " " << ans;
return 0;
}
思想就是不断增加1 的个数,判断是否满足条件。高精度的题,类似于除法竖式,再后面补1
#include
using namespace std;
const int N = 1e5 + 10;
int ch[N],n;
int check(int mid) {
int init = mid;
for (int i = 1; i <= n; ++i) {
if (ch[i] > init) init = init - (ch[i] - init);
else init = init + (init - ch[i]);
if (init < 0) return 0;
if (init > 1e5+10) return 1;//不加这一句就错了啊,因为会爆longlong
}
return 1;
}
int main(void) {
int maxx=0;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> ch[i];
if (ch[i] > maxx) maxx = ch[i];
}
int l = 1, r = maxx + 1;
while (l < r) {
int mid = (l + r) / 2;
//cout <
if (check(mid)==1) r = mid;
else l = mid + 1;
}
cout << l;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int ch[N];
ll fact[N], infact[N];
int a, b, m;
const int mod = 1e9 + 7;
int check(int x) {
while (x > 0) {
if (x % 10 == a || x % 10 == b) x = x / 10;
else return 0;
}
return 1;
}
ll qpow(ll base, ll pow) {//快速幂
ll ans = 1;
while (pow) {
if (pow & 1) ans = ans * base % mod;
base = base * base % mod;
pow >>= 1;
}
return ans;
}
void init() {
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i++) {
//阶乘预处理,打表
fact[i] = fact[i - 1] * i % mod;
//逆元 i 的 mod-2次方就是 i的模 mod的逆元
infact[i] = infact[i - 1] * qpow(i, mod - 2) % mod;
}
}
ll c(int n, int k) {
if (k<0 || k>n) return 0;
return fact[n] * infact[k] % mod * infact[n - k] % mod;
}
signed main(void) {
init();
cin >> a >> b >> m;
int ans = 0,cnt=0;
if (a > b) swap(a, b);
for (int i = 1; i <= m+1; ++i) {
ch[i] = (b - a) * (i-1) + a*m;
if (check(ch[i])) {
cnt++;
ans = (ans + c(m, i-1) ) % mod;
}
}
cout << ans;
/*cout << cnt;
cout << "\n";
cout << c(6,5)<<"\n"<
return 0;
}
枚举总和,如果满足条件,求组合数(求组合数的板子)
#include
using namespace std;
int ch[200][200];
int kk[200];
int flag[200];
int main(void) {
int n, k, x;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> kk[i];
for (int j = 1; j <= kk[i]; ++j) {
cin >> ch[i][j];
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) { //还是要从1开始,不能从i+1开始
if (j == i) continue;
int sum = 0;
for (int k = 1; k <= kk[i]; ++k) {
for (int l = 1; l <= kk[j]; ++l) {
if (ch[i][k] == ch[j][l]) {
sum++;
break;//要break,因为有重复的,这时继续加就会出错
}
}
}
if (sum == kk[i]) flag[j] = 1;
}
}
for (int i = 1; i <= n; ++i) {
if (flag[i]) cout << "NO\n";
else cout << "YES\n";
}
return 0;
}
模拟题,就按题目意思写就行,还是有所收获的,就比如for循环的顺序,范围
#include
#include
#include
using namespace std;
string s[109];
int len[109];
int main(void) {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> len[i];
cin >> s[i];
}
for (int i = 1; i <= n; ++i) {
string sssss = s[i];
//string str = s.reserve();这种写法根本没翻转
reverse(sssss.begin(), sssss.end());
//cout << sssss << "\n";
int flag = 0;
for (int j = 0; j < len[i]; ++j) {
//cout << s[i].substr(len[i] - j) << " " << s[i].substr(0, len[i] - j) << "\n";
if((s[i].substr(len[i] - j)+s[i].substr(0,len[i]-j))==sssss) flag=1;
}
if(flag) cout<<"YES\n";
else cout<<"NO\n";
}
return 0;
}
首先要转化一下的,子串翻转后要成为循环子串,那么只要满足原串翻转后与循环相等即可
然后学到了字符串翻转的写法
#include
#include
#include
#include
#include
using namespace std;
const int N = 2e5 + 10;
map<int, int>mpl;//记录第i行往左走的最大值
map<int, int>mpr;//记录第i行往右走的最小值
struct node {
int x;
int y;
}point[N];
//找每一行中往左走的最大值,和往右走的最小值,如果maxx>min,就会产生碰撞
int main(void) {
int n;
string str;
cin >> n;
for (int i = 0; i < n; ++i) {
cin >> point[i].x >> point[i].y;
mpl[point[i].y] = -1;
mpr[point[i].y] = 1e9;
}
cin >> str;
for (int i = 0; i < n; ++i) {
if (str[i] == 'L') {
mpl[point[i].y] = max(point[i].x, mpl[point[i].y]);
if (mpr[point[i].y] < point[i].x) {
cout << "Yes";
return 0;
}
}
if (str[i] == 'R') {
mpr[point[i].y] = min(point[i].x, mpr[point[i].y]);
if (mpl[point[i].y] > point[i].x) {
cout << "Yes";
return 0;
}
}
}
cout << "No";
return 0;
}
如果某一时刻满足条件就可以直接输出return了
还有一种做法,就是读取完后再进行操作
#include
#include
#include
#include
#include
using namespace std;
const int N = 2e5 + 10;
map<int, int>mpl;//记录第i行往左走的最大值
map<int, int>mpr;//记录第i行往右走的最小值
struct node {
int x;
int y;
}point[N];
//找每一行中往左走的最大值,和往右走的最小值,如果maxx>min,就会产生碰撞
int main(void) {
int n;
string str;
cin >> n;
for (int i = 0; i < n; ++i) {
cin >> point[i].x >> point[i].y;
mpl[point[i].y] = -1;
mpr[point[i].y] = 1e9;
}
cin >> str;
for (int i = 0; i < n; ++i) {
if (str[i] == 'L') mpl[point[i].y] = max(mpl[point[i].y], point[i].x);
else mpr[point[i].y] = min(mpr[point[i].y], point[i].x);
}
/*for (auto t : mpl) {
cout << t.first << " " << t.second<<" ";
}
cout << "\n";
for (auto t : mpr) {
cout << t.first << " " << t.second<<" ";
}*/
for (auto t : mpr) {
if (t.second == 1e9) continue;
if (t.second < mpl[t.first]) {
cout << "Yes";
return 0;
}
}
cout << "No";
return 0;
}
建议以后使用map循环时,先输出答案看看,这里就发现他是从0开始的,直到有出现的最大的,就是说没有出现的也会在for循环里面
动态规划做法:要理解转移方程,挺难的
#include
#include
#include
#include
#include
using namespace std;
const int N = 2e3 + 10;
int ch[N][N];
int dp[N][N];
int main(void) {
int n, t;
int x, y;
cin >> n >> t;
for (int i = 1; i <= t; ++i) {
cin >> x >> y;
ch[x][y] = 1;
}
int maxx = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (ch[i][j] == 0) {
dp[i][j] = min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1]))+1;
if (dp[i][j] > maxx) maxx = dp[i][j];
}
}
}
cout << maxx;
return 0;
}
类似题目
洛谷1387最大正方形
输入一个矩阵,有0有1
找到为1的最大正方形
解法:前缀和+暴力枚举边长
优化:从大到小枚举
#include
#include
#include
#include
#include
using namespace std;
const int N = 2e3 + 10;
int ch[N][N];
int cnt[N][N];
int main(void) {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> ch[i][j];
cnt[i][j] = cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1] + ch[i][j];//前缀和
}
}
int ans = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
for (int k = ans; k <= min(n, m); ++k) {//为什么这里是从小到大枚举,而下面的暴力是从大到小枚举,因为如果这里从大到小枚举的话,最大的满足不了,跳出循环,就找下一个点去了,而下面暴力解法的跳出循环是跳到k-1
if (i + k > n || j + k > m || (cnt[i + k][j + k]-cnt[i-1][j+k]-cnt[i+k][j-1]+cnt[i-1][j-1] != ((k+1)*(k+1)))) break;
ans = max(ans, k+1);
}
}
}
cout << ans;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
int ch[101][101];
int cnt[1010][1010];
int main(void) {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
scanf("%d", &ch[i][j]);
cnt[i][j] = cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1] + ch[i][j];
}
}
int ans = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
for (int k = min(n,m); k >=ans; --k) {
if (i + k > n || j + k > m) continue;//如果从大到小枚举的话,应该是相等跳出循环
if (i + k <= n&& j + k <= m && (cnt[i + k][j + k] - cnt[i - 1][j + k] - cnt[i + k][j - 1] + cnt[i - 1][j - 1]) == ((k + 1) * (k + 1))) {
ans = k + 1;
break;
}
}
}
}
cout << ans;
return 0;
}
暴力解法
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
int ch[101][101];
int main(void) {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
scanf("%d", &ch[i][j]);
}
}
int ans = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
for (int k = min(n, m); k >= ans; --k) {
int p = 0;
for (int x = i; x < i + k; ++x) {
for (int y = j; y < j + k; ++y) {
if (x>n||y>m||ch[x][y] == 0) p = 1;
if (p == 1) break;
}
if (p == 1) break;
}
if (p == 0) ans = k;
}
}
}
cout << ans;
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
const int N = 2e3 + 10;
int ch[N][N];
int cnt[N][N];
const double eps = 1e-8;
int n, m, k;
double check(double mid) {
double sum = 0;
for (int i = 1; i <= k; ++i) {
sum = sum + m / pow(1 + mid, i);
}
if (sum >= n) return 1;
return 0;
}
int main(void) {
cin >> n >> m >> k;
double l = 0, r = 5;
for (int i = 1; i <= 100; ++i) {
double mid = (l + r) / 2;
if (check(mid)) l= mid;
else r = mid;
}
//cout << l;
printf("%.6f", l);//卡精度的题看来得用printf卡小数点位数,反正我cout过不了
return 0;
}
#include
#include
#include
#include
#include
#include
using namespace std;
#define int long long
const int N = 2e5 + 10;
int ch[N];
int n, m, k;
signed main(void) {
int t, n, w;
int check;
cin >> t;
while (t--) {
cin >> n >> w;
if (w % 2 == 0) check = w / 2;
else check = (w + 1) / 2;
int flag = 0;
for (int i = 1; i <= n; ++i) {
cin >> ch[i];
if (ch[i] >= check && ch[i] <= w) flag = 1;
}
if (flag) {
cout << "YES" << "\n";
continue;
}
sort(ch + 1, ch + 1 + n);
int sum = 0;
//for循环从前往后,或者从后往前都可以
for (int i = n; i >=1; --i) {
sum += ch[i];
if (sum > w) sum -= ch[i];
else if (sum >= check) {
flag = 1;
break;
}
}
if (flag) cout << "YES\n";
else cout << "NO" << "\n";
}
return 0;
}
这里要搞清楚的是肯定是连续的,而不是间断的,即不存在123超过w但是13满足的情况,因为如果说123超了,就说明12肯定满足了,因为这时的每个物品的体积都小于w/2(因为如果有一个物品体积>w/2&& 有一个结论就是,从0到 2 n − 1 2^n-1 2n−1的异或和是0(在n不为1时) ,然后假如你要得到x,只要异或和时不取x即可。 若 m 异或 x = 0 m异或x=0 m异或x=0 则m=x 为什么想到这个呢,因为题目要求最多,所以要想到全部都选是什么情况,还有就是这个数给的挺特殊,关于2的几次方 与往常的dp不太一样,求最长上升子序列是枚举i, 706上帝的集合 (好题) 简单的模拟题 在一个地图上有N个地窖(N<=20),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。 当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。 W1 W2 W3 …… WN(表示每个地窖中埋藏的地雷数量) A12…………… . A1N(地窖之间连接路径,其中Aij=1表示地窖i,j之间是否有通路:通Aij=1,不通Aij==0) A23……………A2N ……… AN-1 N703 简单的异或问题
#include
605优美!最长上升子序列
#include
for int j=1;j
#define _CRT_SECURE_NO_WARNINGS 1
#include
定义大根堆的方法需要记一下的,还有就是全部元素加上一个数,如果循环逐个加的话肯定会炸,所以定义一个tag来存储现在加了多少,但是新插入一个数时push的时候要减去当前的tag,输出的时候要加上tag
还有就是#define int long long 然后scanf里面要用lld别忘了
704 子串的循环挪动
#define _CRT_SECURE_NO_WARNINGS 1
#include
挖地雷
#include