A:
题意:
给一堆单词(用’ \space ’分开),问所有单词中单个单词大写字母最多有多少\
题解:
没什么好说的了主要是读懂题意
代码:
#include<bits/stdc++.h>
using namespace std;
int n;char ch[222];
signed main(){
scanf("%d",&n);int ans = 0;
while(scanf("%s",ch + 1) != EOF){
int cnt = 0;for(int i = 1;i <= strlen(ch + 1);i++){if('A' <= ch[i] && ch[i] <= 'Z')cnt++;}
ans = max(ans,cnt);
}
printf("%d\n",ans);
return 0;
}
B:
题意:
Berland之旗是满足以下条件的 n×mn \times mn×m 矩形区域:
-
旗中包含三种颜色,相应地以字母“R”, “G”和“B”表示。
-
旗中包含三个“长条”,他们的长度和宽度相等(长条1的长度等于长条2的长度,也等于长条3的长度,宽度类似),并且互相平行,同时还平行于旗子的边缘。每个“长条”仅有一种颜色。
-
每个颜色仅能在一个“长条”中出现。
现在给定由“R”, “G”和“B”构成的 n×mn \times mn×m 矩形区域。请判断它是否是一个正确的Berland之旗,是则输出 “YES”,否则输出"NO"。
题解:
所谓的??之旗就是让你找一个 nnn%3==03==03==0 或者 mmm%3==03==03==0,然后判断一下是不是分成三部分(想象一下用刀沿着n或m平均切三刀)每一部分颜色都相同且不重复
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
int n, m;char ch[maxn][maxn];int a[maxn][maxn];
bool book[4];
bool check(int x,bool mod){
if(mod){
int col = a[1][m / 3 * x + 1];if(book[col])return 0;book[col] = 1;
for(int i = 1;i <= n;i++){
for(int j = m / 3 * x + 1;j <= m / 3 * (x + 1);j++){
// printf("%d %d\n",i,j);
if(a[i][j] != col)return false;
}
}
return true;
}
else{
int col = a[n / 3 * x + 1][1];if(book[col])return 0;book[col] = 1;
for(int i = 1;i <= m;i++){
for(int j = n / 3 * x + 1;j <= n / 3 * (x + 1);j++){
if(a[j][i] != col)return false;
}
}
return true;
}
}
signed main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++){
scanf("%s",ch[i] + 1);
for(int j = 1;j <= m;j++)
a[i][j] = (ch[i][j] == 'R' ? 1 : (ch[i][j] == 'G' ? 2 : 3));
}
bool ans = 0;
if(m % 3 == 0){
bool res = 1;memset(book,0,sizeof(book));
for(int i = 0;i <= 2;i++)if(!check(i,1)){res = 0;break;}
ans |= res;
}
if(n % 3 == 0){
bool res = 1;memset(book,0,sizeof(book));
for(int i = 0;i <= 2;i++)if(!check(i,0)){res = 0;break;}
ans |= res;
}
puts(ans ? "YES" : "NO");
return 0;
}
C:
题意:
给一个 a∗ba*ba∗b 的大矩形和 nnn 个 xi∗yix_i*y_ixi∗yi 的小矩形(可旋转 90°90°90° ),要求找两个小矩形塞进大矩形中并且面积之和最大
题解:
放矩形的时候让一个的左上角顶在 (1,1)(1,1)(1,1) ,另一个的右下角顶在 (a,b)(a,b)(a,b) ,判断能不能放下就好了
代码:
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 110;
int x[maxn], y[maxn];
int n, a, b;
bool check(int xi,int yi,int xj,int yj){
if(xi > a || xj > a || yi > b || yj > b)return false;
if(xi + xj > a && yi + yj > b)return false;
return true;
}
signed main(){
int ans = 0;n = read();a = read(); b = read();
for(int i = 1;i <= n;i++)x[i] = read(), y[i] = read();
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
if(i == j)continue;
if(check(x[i],y[i],x[j],y[j]))ans = max(ans,x[i] * y[i] + x[j] * y[j]);
if(check(y[i],x[i],x[j],y[j]))ans = max(ans,x[i] * y[i] + x[j] * y[j]);
if(check(x[i],y[i],y[j],x[j]))ans = max(ans,x[i] * y[i] + x[j] * y[j]);
if(check(y[i],x[i],y[j],x[j]))ans = max(ans,x[i] * y[i] + x[j] * y[j]);
}
}
printf("%d\n",ans);
return 0;
}
D:
题意:
给你n个数,选k个数相乘使得结果的后缀0最多
题解:
幼儿园大班芝士可知后缀0由质因子中的2和5决定
于是乎定义 dpi,j,kdp_{i,j,k}dpi,j,k 表示取到第 iii 个数,已经取了 jjj 个数, 555 的质因子有 kkk 个时 222 的质因子最大值
然后dpi,j,kdp_{i,j,k}dpi,j,k = min(dpi−1,j,k,dpi−1,j−1,k−cnt5i+cnt2i)min(dp_{i-1,j,k},dp_{i-1,j-1,k-cnt5_i}+cnt2_i)min(dpi−1,j,k,dpi−1,j−1,k−cnt5i+cnt2i)
初始条件 dp0,0,0=0dp_{0,0,0}=0dp0,0,0=0 其余为 −INF-INF−INF
然后显然发现这个是01背包可以把i维度优化掉
然后就没有然后了
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 210;
int n, k;int a[maxn], b[maxn];
int dp[maxn][12810];//dp[i][j][k] = 当前到第i个数,选了j个数,5的因数为k个时2的因数最大值
signed main(){
n = read();k = read();int x = 0;
for(int i = 1;i <= n;i++){
x = read();if(!x)continue;
while(x % 2 == 0){a[i]++;x /= 2;}
while(x % 5 == 0){b[i]++;x /= 5;}
}
memset(dp,~0x3f,sizeof(dp)); dp[0][0] = 0;
for(int i = 1;i <= n;i++)
for(int j = k;j >= 1;j--)
for(int l = 12800;l >= b[i];l--)
dp[j][l] = max(dp[j][l],dp[j - 1][l - b[i]] + a[i]);
int ans = 0;
for(int i = 0;i <= 12800;i++){ans = max(ans,min(i,dp[k][i]));}
printf("%lld\n",ans);
return 0;
}
E:
题意:
定义一个函数
f(n,m)={0, (m=0)1+f(n,m−gcd(n,m)), (m≠0)f(n,m) =
\begin{cases}
0,\,\,(m=0)\\
1+f(n,m-gcd(n,m)),\,\,(m\neq0)\\
\end{cases}f(n,m)={0,(m=0)1+f(n,m−gcd(n,m)),(m=0)
然后给出nnn和mmm,求f(n,m)f(n,m)f(n,m)
题解:
首先有一个推论 f(n,m)=f(ngcd(n,m),mgcd(n,m))f(n,m)=f(\frac{n}{gcd(n,m)},\frac{m}{gcd(n,m)})f(n,m)=f(gcd(n,m)n,gcd(n,m)m)
然后问题就变成了每次求最大的x<=mx<=mx<=m使得x∣nx|nx∣n然后f(n,m)=x+f(n,x)f(n,m)=x+f(n,x)f(n,m)=x+f(n,x)
这个好办,只需要把n的质因数都找出来(O(log2n))O(\log_2n))O(log2n))的
然后每次更新时只需要暴力枚举找到max(mmax(mmax(m%i)i)i),然后x就是前面的max值
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a, b;
const int maxn = 1e6 + 10;
bool book[maxn];int prime[maxn], cnt;
int x[maxn], p[maxn], tot;
int gcd(int x,int y){return y == 0 ? x : gcd(y,x % y);}
int f(int n,int m){
int ans = 0;
while(1){
int mx = 0, pos = 0;
for(int i = 1;i <= tot && x[i] <= m;i++)
if(p[i] && (m / x[i] * x[i] > mx))mx = m / x[i] * x[i], pos = i;
ans += m - mx;m = mx;
if(!m)return ans;
// if(m == 1){return ans + 1;}
--p[pos];n /= x[pos]; m /= x[pos];
}
return -114514;
}
signed main(){
scanf("%lld%lld",&a,&b);
// book[1] = 1;int g = gcd(a, b);a /= g;b /= g;
for(int i = 2;i <= 1e6;i++){
if(!book[i]){prime[++cnt] = i;}
for(int j = 1;j <= cnt && i * prime[j] <= 1e6;j++){
book[i * prime[j]] = 1;
if(i % prime[j] == 0)break;
}
}
// for(int i = 1;i <= 10;i++)printf("prime[%lld] = %lld\n",i,prime[i]);
int tmp = a;
// tot++;x[tot] = 1;p[tot] = 1e18;
for(int i = 1;i <= cnt && tmp >= prime[i];i++){
if(tmp % prime[i] == 0){
tot++;x[tot] = prime[i];while(tmp % prime[i] == 0){tmp /= prime[i];p[tot]++;}
}
}
if(tmp != 1){tot++;x[tot] = tmp;p[tot] = 1;}
// printf("%lld\n",tot);
// for(int i = 1;i <= tot;i++)printf("x[%lld] = %lld,p[%lld] = %lld\n",i,x[i],i,p[i]);
printf("%lld\n",f(a,b));
return 0;
}
F:
题意:
给一个数组aaa,每次操作让新的aia_iai赋值为原来的∑j=1iaj\sum_{j=1}^{i}a_j∑j=1iaj,问最少多少次操作使得∃i,ai>=k\exist i,a_i>=k∃i,ai>=k
题解:
首先对于n==2n==2n==2的情况可以O(1)O(1)O(1)的计算答案(手推一下柿子就好了),对于n>10n>10n>10的情况可以暴力计算次数(由题解得最多计算410次),而对于2<n<=102<n<=102<n<=10的情况可以用二分答案+矩阵快速幂
细节:
- 原数组中的前导零无意义,直接删除
- 乘法需要用龟速乘并且大于kkk的数字没有乘的必要直接等于kkk即可
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f= 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 2e5 + 10;
int n, k;
int a[maxn], tmp[maxn];
inline int mul(int a,int b){
int res = 0;
while(b){
if(b & 1)res = res + a;
if(res > k)return k;
a = a + a;b >>= 1;a = min(k, a);
}
return res;
}
struct Matrix{
int a[11][11];
Matrix(){memset(a,0,sizeof(a));}
friend Matrix operator * (Matrix a,Matrix b){
Matrix ans;
for(int i = 1;i <= n;i++)
for(int t = 1;t <= n;t++)
for(int j = 1;j <= n;j++){
ans.a[i][j] += mul(a.a[i][t] , b.a[t][j]);
if(ans.a[i][j] > k)ans.a[i][j] = k;
}
return ans;
}
}A, B;
Matrix qpow(Matrix a,int x){
Matrix res = a;x--;
while(x){
if(x & 1){res = res * a;}
a = a * a;x >>= 1;
// printf("x= %lld\n",x);
}
return res;
}
bool check(int mid){
Matrix res = A * qpow(B,mid);
// puts("1111111111");
for(int i = 1;i <= n;i++){if(res.a[1][i] >= k)return true;}
return false;
}
signed main(){
// #ifndef ONLINE_JUDGE
// freopen("CF837F.in","r",stdin);
// #endif
n = read(); k = read();
for(int i = 1;i <= n;i++)a[i] = read();
int pos1 = 0, j = 1, book = 1;
for(int i = 1;i <= n;i++){if(a[i] >= k){puts("0");return 0;}if(!a[i] && book)pos1 = i;else book = 0;}
for(int i = pos1 + 1;i <= n;i++,j++){tmp[j] = a[i];}j--;
for(int i = 1;i <= j;i++)a[i] = tmp[i];n = j;
// printf("%lld\n",n);
// for(int i = 1;i <= n;i++){printf("%lld ",a[i]);}
if(n > 10){
int cnt = 0;
while(1){
tmp[0] = 0;cnt++;
for(int i = 1;i <= n;i++)tmp[i] = tmp[i - 1] + a[i];
for(int i = 1;i <= n;i++){
a[i] = tmp[i];
if(a[i] >= k){printf("%lld\n",cnt);return 0;}
}
}
}
else if(n == 2){
// if(a[2] >= k || a[1] >= k){puts("0");return 0;}
int ans = (k - a[2]) / a[1] - 3;
while(1){//精度修正
if(ans * a[1] + a[2] >= k){printf("%lld\n",ans);return 0;}
ans++;
}
}
else{
for(int i = 1;i <= n;i++){A.a[1][i] = a[i];}
for(int i = 1;i <= n;i++)
for(int j = n;j >= i;j--)
B.a[i][j] = 1;
int l = 1, r = 64356879284, mid = 0, ans = -1;
// printf("n = %lld",n);
while(l <= r){
// printf("l=%lld r=%lld\n",l,r);
mid = (r - l) / 2 + l;
// printf("mid=%lld\n",mid);
if(check(mid)){ans = mid;r = mid - 1;}
else l = mid + 1;
}
printf("%lld\n",ans);
}
return 0;
}
G:
题意:
给出n个分段函数形如
fi(x)={y1, (x<=x1)a∗x+b, (x1<x<=x2)y2, (x>x2)f_i(x)={
\begin{cases}
y_1,\,\,(x<=x_1)\\
a*x+b,\,\,(x_1<x<=x_2)\\
y_2,\,\,(x>x_2)
\end{cases}
}fi(x)=⎩⎨⎧y1,(x<=x1)a∗x+b,(x1<x<=x2)y2,(x>x2)
现在给出l,r,xl,r,xl,r,x,求∑i=lrfi(x)\sum_{i=l}^rf_i(x)∑i=lrfi(x),强制在线
题解:
首先先将原函数看作以下形式
fi(x)={0∗x+y1, (x<=x1)a∗x+b, (x1<x<=x2)0∗x+y2, (x>x2)f_i(x)={
\begin{cases}
0*x+y_1,\,\,(x<=x_1)\\
a*x+b,\,\,(x_1<x<=x_2)\\
0*x+y_2,\,\,(x>x_2)
\end{cases}
}fi(x)=⎩⎨⎧0∗x+y1,(x<=x1)a∗x+b,(x1<x<=x2)0∗x+y2,(x>x2)
然后将所求柿子转化一下
∑i=lrfi(x)=∑i=lrai∗x+∑i=lrbi\sum_{i=l}^rf_i(x)=\sum_{i=l}^ra_i*x+\sum_{i=l}^rb_i∑i=lrfi(x)=∑i=lrai∗x+∑i=lrbi
\space \space \space \space \space \space \space \space \space \space =∑i=1rai∗x+∑i=1rbi−∑i=1l−1ai∗x+∑i=1l−1bi=\sum_{i=1}^ra_i*x+\sum_{i=1}^rb_i-\sum_{i=1}^{l-1}a_i*x+\sum_{i=1}^{l-1}b_i=∑i=1rai∗x+∑i=1rbi−∑i=1l−1ai∗x+∑i=1l−1bi
然后发现这玩应似乎可以可持久化主席树
然后就没啥了(那你还调了一下午)\
细节:
- 线段树数组范围能开多大就多大(
反正内存限制接近1GB)
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 75100, mod = 1e9;
int n, m;
int a[maxn][3], b[maxn][3], x[maxn][2];
struct Segment_Tree{
struct node{
int l, r;int a, b;
node(int a = 0,int b = 0,int l = 0,int r = 0):a(a),b(b),l(l),r(r){}
}d[maxn * 160];
int tot;
int newnode(int p){d[++tot] = d[p];return tot;}
void update(int &p,int l,int r,int s,int t,int a,int b){
p = newnode(p);
if(s <= l && r <= t){d[p].a += a;d[p].b += b;return;}
int mid = l + r >> 1;
if(s <= mid)update(d[p].l,l,mid,s,t,a,b);
if(mid < t)update(d[p].r,mid + 1,r,s,t,a,b);
}
int query(int p,int l,int r,int x){
if(!p)return 0;
int res = x * d[p].a + d[p].b;
if(l == r)return res;
int mid = l + r >> 1;
if(x <= mid)res += query(d[p].l,l,mid,x);
else res += query(d[p].r,mid + 1,r,x);
return res;
}
}tree;
int head[maxn << 2], sum[maxn];
signed main(){
n = read();int mx = 0;
for(int i = 1;i <= n;i++){
x[i][0] = read();x[i][1] = read();b[i][0] = read();
a[i][1] = read();b[i][1] = read();b[i][2] = read();
head[i] = head[i - 1];
tree.update(head[i],0,mod,0 ,x[i][0],a[i][0],b[i][0]);
tree.update(head[i],0,mod,x[i][0] + 1,x[i][1],a[i][1],b[i][1]);
tree.update(head[i],0,mod,x[i][1] + 1,mod ,a[i][2],b[i][2]);
sum[i] = sum[i - 1] + b[i][2];mx = max(mx,x[i][1] + 1);
}
m = read();int lastans = 0;
for(int i = 1;i <= m;i++){
int l = read(), r = read(), pos = (read() + lastans) % mod;
if(pos >= mx)printf("%lld\n",lastans = sum[r] - sum[l - 1]);
else printf("%lld\n",lastans = tree.query(head[r],0,mod,pos) - tree.query(head[l - 1],0,mod,pos));
}
return 0;
}
博客包含多道算法题,涉及单词大写字母统计、旗帜判断、矩形放置、数字相乘后缀0、自定义函数求值、数组操作次数及分段函数求和等问题。题解运用了动态规划、二分答案、矩阵快速幂、可持久化主席树等算法,还给出了相应代码。
180





