2024.11.22
今日总结
上午打模拟赛,下午改完了三道题+半道题,最后一题只会到27分的做法,其他的看不懂晚上做了两年NOIP其中的真题
1:集邮比赛3
这道题是一道很显然的区间Dp题目
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 210;
const int INF = 1e18;
int n,L,ans;
int x[N],t[N],dp[N][N][N][3];
signed main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
scanf("%lld%lld",&n,&L);
for(int i = 1;i <= n;i ++)
scanf("%lld",&x[i]);
for(int i = 1;i <= n;i ++)
scanf("%lld",&t[i]);
memset(dp,0x3f,sizeof(dp));
x[n + 1] = L;
dp[0][0][0][0] = dp[0][0][0][1] = 0;
for(int l = 0;l <= n;l ++)
{
for(int r = 0;r <= n;r ++)
{
if(l + r >= n) break;
for(int k = 0;k <= n;k ++)
{
if(dp[l][r][k][0] <= INF)
{
dp[l + 1][r][k + (dp[l][r][k][0] + x[n - l + 1] - x[n - l] <= t[n - l])][0] = min(dp[l + 1][r][k + (dp[l][r][k][0] + x[n - l + 1] - x[n - l] <= t[n - l])][0],dp[l][r][k][0] + x[n - l + 1] - x[n - l]);
dp[l][r + 1][k + (dp[l][r][k][0] + L - x[n - l + 1] + x[r + 1] <= t[r + 1])][1] = min(dp[l][r + 1][k + (dp[l][r][k][0] + L-x[n - l + 1] + x[r + 1] <= t[r + 1])][1],dp[l][r][k][0] + L - x[n - l + 1] + x[r + 1]);
}
if(dp[l][r][k][1] <= INF)
{
dp[l + 1][r][k + (dp[l][r][k][1] + L - x[n - l] + x[r] <= t[n - l])][0]=min(dp[l + 1][r][k + (dp[l][r][k][1] + L - x[n - l] + x[r] <= t[n - l])][0],dp[l][r][k][1] + L - x[n - l] + x[r]);
dp[l][r + 1][k + (dp[l][r][k][1] + x[r + 1] - x[r] <= t[r + 1])][1] = min(dp[l][r + 1][k + (dp[l][r][k][1] + x[r + 1] - x[r] <= t[r + 1])][1],dp[l][r][k][1] + x[r + 1] - x[r]);
}
}
}
}
for(int l = 0;l <= n;l ++)
for(int r = 0;r <= n;r ++)
for(int k = 0;k <= n;k ++)
if(min(dp[l][r][k][0],dp[l][r][k][1]) < INF) ans = max(ans,k);
printf("%lld\n",ans);
return 0;
}
2:不允許打手槍
这道题是一道需要分类讨论的状态机Dp的题目,只需要处理好每个点可以进行的状态,在进行转移,用滚动数组优化一下,但一定还要对范围进行优化,否则会TLE最后几个点,不要尝试写O(1e9)复杂度的代码,一定会被卡
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 46,K = 91;
const int Mod = 998244353;
int n,m,k;
int a[N][N],b[N][N];
int dp[3][N][N][N][K];
void Clear(int i,int fa)
{
for(int j = 1;j <= m;j ++)
for(int L = 0;L <= n - i;L ++)
for(int Z = 0;Z <= m - j;Z ++)
for(int p = 0;p <= k;p ++)
dp[fa][j][L][Z][p] = 0;
}
int main()
{
freopen("railway.in","r",stdin);
freopen("railway.out","w",stdout);
function<int(int,int)>add = [&](int x,int y)
{
long long p = (long long)x + (long long)y;
if(p >= Mod) p -= Mod;
return (int)p;
};
scanf("%d%d%d",&n,&m,&k);
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++)
scanf("%d",&a[i][j]);
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++)
scanf("%d",&b[i][j]);
dp[1][1][0][0][k] = 1;
int u = 1,fa = 0;
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= m;j ++)
{
for(int L = 0;L <= n - i;L ++)
{
for(int Z = 0;Z <= m - j;Z ++)
{
for(int p = 0;p <= k;p ++)
{
if(i > 1) dp[u][j][L][Z][p] = add(dp[u][j][L][Z][p],dp[fa][j][L + 1][Z][p]) ; // 用L
if(j > 1) dp[u][j][L][Z][p] = add(dp[u][j][L][Z][p],dp[u][j - 1][L][Z + 1][p]) ; // 用Z
if(L > 0 && Z > 0 && p + a[i][j] + b[i][j] <= k) dp[u][j][L][Z][p] = add(dp[u][j][L][Z][p],(Mod - dp[u][j][L - 1][Z - 1][p + a[i][j] + b[i][j]])); // 买L和Z
if(L > 0 && p + a[i][j] <= k) dp[u][j][L][Z][p] = add(dp[u][j][L][Z][p],dp[u][j][L - 1][Z][p + a[i][j]]) ; // 买L
if(Z > 0 && p + b[i][j] <= k) dp[u][j][L][Z][p] = add(dp[u][j][L][Z][p],dp[u][j][L][Z - 1][p + b[i][j]]); // 买Z
}
}
}
printf("%lld ",dp[u][j][0][0][0]);
}
printf("\n");
Clear(i,fa);
u ^= 1,fa ^= 1;
}
return 0;
}
3:我們學生躲在遊戲屋,不覺得羞愧?
一旦看到这种新定义的题目,不要以为是很难的数学知识,先简单的推一推,也有可能是需要对式子进行找规律进行if判断就能推出来
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int T,n,k;
void read(int &v)
{
v = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
ch = getchar();
while(ch >= '0' && ch <= '9')
{
v = v * 10 + ch - 48;
ch = getchar();
}
}
int work()
{
if(k == 1) return 0;
int ans = 0;
if(k >= 4)
{
++ ans;
if(n > 2) ans += n - 2;
}
if(n >= 2)
{
if(k >= 5) ++ ans;
if(k >= 4) ++ ans;
}
if(k >= 4) if(n > 3) ans += n - 3;
if(k >= 5 && n >= 3) ++ ans;
if(k == 2) ++ ans;
return ans;
}
void write(int x)
{
if(x < 10)
{
putchar(x + '0');
return;
}
write(x / 10);
putchar(x % 10 + '0');
}
signed main()
{
freopen("set.in","r",stdin);
freopen("set.out","w",stdout);
scanf("%d",&T);
while(T --)
{
read(n),read(k);
write(work());
putchar('\n');
}
return 0;
}
4:傳自我的手機
这道题正解应该是要用到线段树合并和并查集,我只写了一个用并查集的暴力解法
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e3 + 10;
const int M = N * 2;
int c,n,q,u,v,opt,x,y,tot;
int a[M],bel[M],vis[M],gx[M];
int ans[M],f[M];
set<int> s[M];
int Find(int x)
{
if(x == f[x]) return x;
return f[x] = Find(f[x]);
}
void dfs(int x,int fa)
{
if(vis[x]) return;
vis[x] = tot;
for(auto y : s[x])
if(y != fa) dfs(y,x);
}
signed main()
{
freopen("component.in", "r", stdin);
freopen("component.out", "w", stdout);
scanf("%lld%lld%lld%lld%ld",&c,&n,&q,&u,&v);
int Mod = pow(u,v);
for(int i = 1;i <= n;i ++) scanf("%lld",&a[i]);
if(c <= 3)
{
for(int i = 1;i <= q;i ++)
{
scanf("%lld",&opt);
if(opt == 1)
{
scanf("%d%d",&x,&y);
s[x].insert(y),s[y].insert(x);
}
else if(opt == 2)
{
scanf("%d%d",&x,&y);
s[x].erase(y),s[y].erase(x);
}
else if(opt == 3)
{
scanf("%d%d",&x,&y);
a[x] = y;
}
else
{
scanf("%d",&x);
tot = 0;
memset(vis,0,sizeof(vis));
for(int i = 1;i <= n;i ++)
{
if(!vis[i])
{
tot ++;
dfs(i,0);
}
}
for(int i = 1;i <= tot;i ++)
gx[i] = 1;
for(int i = 1;i <= n;i ++)
{
gx[vis[i]] *= a[i] + x;
gx[vis[i]] %= Mod;
}
int ans = 0;
for(int i = 1;i <= tot;i ++)
{
ans += gx[i];
ans %= Mod;
}
printf("%lld\n",ans);
}
}
}
if(c == 4)
{
int ret = 0;
for(int i = 1;i <= n;i ++)
{
f[i] = i;
ans[i] = a[i];
ret += a[i];
ret %= Mod;
}
for(int i = 1;i <= q;i ++)
{
scanf("%lld",&x);
if(opt == 1)
{
scanf("%lld%lld",&x,&y);
int fx = Find(x),fy = Find(y);
if(fx != fy)
{
f[fx] = fy;
ret -= ans[fx];
ret -= ans[fy];
ans[fy] = ans[fx] * ans[fy] % Mod;
ret += ans[fy];
ret = (ret % Mod + Mod) % Mod;
ans[fx] = 0;
}
}
else if(opt == 2 || opt == 3) scanf("%lld%lld",&x,&y);
else
{
scanf("%lld",&x);
printf("%lld\n",ret);
}
}
}
return 0;
}
5:幂次
这道题与今天考试的第三题类型相同,都是需要推一推公式进行判断就能出来结果
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 110;
const long double eps = 1e-18;
int n,k,ans = 1;
int f[N];
signed main()
{
scanf("%lld%lld",&n,&k);
for(int i = 100;i >= k;i --)
{
f[i] = pow<long double>(n,1.0 / i) + eps - 1;
for(int j = i << 1;j <= 100;j += i) f[i] -= f[j];
ans += f[i];
}
printf("%lld\n",ans);
return 0;
}
6:种花
这道题其实是一道模拟+前缀和的题目,但这道题难的是需要在前缀和上求前缀和,很新颖,并且在遍历的同时,记录前缀和,思路很好想,但是在写代码的时候细节很多,需要维护的东西很多,尤其是加上了横纵坐标
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
const int Mod = 998244353;
int n,m,c,f,C,F,jil,jis,jilf;
int s[N][N];
char mp[N][N];
int main()
{
// freopen("plant.in","r",stdin);
// freopen("plant.out","w",stdout);
int T,id;
scanf("%d%d",&T,&id);
while(T --)
{
memset(s,0,sizeof(s));
scanf("%d%d%d%d",&n,&m,&c,&f);
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++)
cin >> mp[i][j];
for(int i = 1;i <= n;i ++)
{
for(int j = m - 1;j >= 1;j --)
{
if(mp[i][j] == '1') s[i][j] = -1;
else if(mp[i][j + 1] == '0') s[i][j] = s[i][j + 1] + 1;
}
}
for(int j = 1;j < m;j ++)
{
jil = jilf = 0;
for(int i = 1;i <= n;i ++)
{
if(s[i][j] == -1)
{
jil = jilf = 0;
continue;
}
// cout << "jil:" << jil << endl;
C = C % Mod + (1ll * s[i][j] * (jil % Mod)) % Mod; // s[i][j] * jil
// cout << "C:" << C << endl;
F = (F % Mod + jilf % Mod) % Mod;
// cout << "jilf:" << jilf << endl;
// cout << "F:" << F << endl;
jilf = (jilf + (1ll * s[i][j] * (jil % Mod)) % Mod) % Mod;
jil += max(0,s[i - 1][j]);
}
}
printf("%d %d\n",(c * C) % Mod,(f * F) % Mod);
C = F = 0;
}
return 0;
}