7.30 模拟赛总结
做题过程
开场 20 分钟做完第一题,测完大样例再造了组有空格的数据就没管了。接着是第二题,一眼 dp,然后我写了递推,花了两个小时调。此时比赛还剩 1 小时,思考第三题,第三题写了暴力,dfs \([l, r]\) 中的每一个 \(x_i\)。第四题啥都没写,剩下0.5h 罚座。
题目解法
A 题可以枚举每个字符串,再枚举每一个长度为二的区间,将其替换为 out ans,看是否可以匹配。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
map<string,int> mp;
string s[105];
int main(){
freopen("judge.in","r",stdin);
freopen("judge.out","w",stdout);
int n,ans=0;
cin>>n;
getchar();
for(int i=1;i<=n;i++){
getline(cin,s[i]);
for(int j=0;j<s[i].size();j++){
if('A'<=s[i][j]&&s[i][j]<='Z') s[i][j]=s[i][j]-'A'+'a';
}
mp[s[i]]++;
}
for(int i=1;i<=n;i++){
for(int l=0,r=1;r<s[i].size();l++,r++){
if(s[i].substr(l,2)!="in") continue;
string s1=s[i].substr(0,l)+"out"+s[i].substr(r+1,s[i].size()-2-l);
string s2=s[i].substr(0,l)+"ans"+s[i].substr(r+1,s[i].size()-2-l);
ans+=mp[s1]+mp[s2];
}
}
cout<<ans;
return 0;
}
B 题显然有 \(dp_{i,j,k,l}=x\) 表示前 \(i\) 个回合,有 \(j\) 的人,\(k\) 的电力,\(l\) 的石油,最多能有 \(x\) 的产品。考虑交换 \(x\) 和 \(k\)。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=15,lim=400;
int p[N],g[N],e[N],h[N];
int f[45][15][405][405];
int n,m,H,task,ans;
void dfs(int t,int hh,int pp,int gg,int ee){
if(f[t][hh][ee][gg]<pp) f[t][hh][ee][gg]=pp;
else return;
ans=max(ans,ee);
if(t>m) return;
for(int i=1;i<=n;i++){
if(hh+h[i]>=0&&pp+p[i]>=0&&gg+g[i]>=0&&ee+e[i]>=0) dfs(t+1,hh+h[i],pp+p[i],gg+g[i],ee+e[i]);
}
}
int main(){
freopen("grid.in","r",stdin);
freopen("grid.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&H,&task);
for(int i=1;i<=n;i++) scanf("%d%d%d%d",&h[i],&p[i],&g[i],&e[i]);
memset(f,-1,sizeof f);
dfs(1,H,0,0,0);
printf("%d",ans);
return 0;
}
C 题根据裴蜀定理,有 \(ax + by = c\) 的有解情况当且仅当 \(\gcd(a,b) | c\),扩展到多个数,有 \(a_1x_1 + a_2x_2 + \dots + a_nx_n = c\) 有解当且仅当 \(\gcd(a_1, a_2, \dots, a_n) | c\),因此得到本题询问的答案为 \(\lfloor\frac{s}{\gcd(a_1, a_2, \dots, a_n)}\rfloor\)。线段树维护即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5;
int n,m;
ll a[N],seg[N<<2];
void push_up(int rt){
seg[rt]=__gcd(seg[rt<<1],seg[(rt<<1)|1]);
}
void build(int rt,int l,int r){
if(l==r){
seg[rt]=a[l];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build((rt<<1)|1,mid+1,r);
push_up(rt);
}
void modify(int rt,int l,int r,int pos,ll val){
if(l==r){
seg[rt]=val;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) modify(rt<<1,l,mid,pos,val);
else modify((rt<<1)|1,mid+1,r,pos,val);
push_up(rt);
}
ll ask(int rt,int l,int r,int L,int R){
if(L<=l&&r<=R) return seg[rt];
int mid=(l+r)>>1;
ll gcd=-1;
if(L<=mid) gcd=ask(rt<<1,l,mid,L,R);
if(mid<R){
ll tmp=ask((rt<<1)|1,mid+1,r,L,R);
if(~gcd) gcd=__gcd(gcd,tmp);
else gcd=tmp;
}
return gcd;
}
void solve(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
memset(seg,0,sizeof seg);
build(1,1,n);
for(int op;m--;){
scanf("%d",&op);
if(op==1){
int i;
ll v;
scanf("%d%lld",&i,&v);
modify(1,1,n,i,v);
}
else{
int l,r;
ll s;
scanf("%d%d%lld",&l,&r,&s);
printf("%lld\n",s/ask(1,1,n,l,r));
}
}
}
int main(){
freopen("equation.in","r",stdin); freopen("equation.out","w",stdout);
int T,task;
scanf("%d%d",&T,&task);
while(T--) solve();
return 0;
}
D 题我们对每个线路暴力建边,边数 \(p^{2}m\) 条,点数 \(n\) 个,用 spfa 求最长路,理论最坏复杂度是 \(n^{2}p^{2}m\),不过这题没卡 spfa。
顺便说一句,在段凡丁的论文里,spfa 的复杂度是 \(\mathcal{O}(m)\) 的。虽然段凡丁错了,但是在大部分情况下,spfa 复杂度的确只在 \(\mathcal{O}(m)\) 左右。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e3+5;
int x[N],y[N],h[N],n,m;
ll dis[N];
bool in[N];
vector<pair<int,ll>> e[N];
ll calc(int x1,int y1,int x2,int y2){
return ceil(sqrt(1ll*(x1-x2)*(x1-x2)+1ll*(y1-y2)*(y1-y2)));
}
int main(){
freopen("metro.in","r",stdin);
freopen("metro.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) scanf("%d%d%d",&x[i],&y[i],&h[i]);
for(int i=1; i<=m; i++){
int p;
scanf("%d",&p);
vector<int> v;
for(int j=0;j<p;j++){
int b,las=0;
scanf("%d",&b);
v.push_back(b);
for(int i=v.size()-2;i>=0;i--){
e[v[i]].push_back({b,(las-=calc(x[v[i]],y[v[i]],x[v[i+1]],y[v[i+1]]))});
}
}
}
for(int i=1; i<=n; i++){
memset(dis,0xc0,sizeof dis);
memset(in,0,sizeof in);
queue<int> q;
q.push(i);
in[i]=1;
dis[i]=h[i];
while(!q.empty()){
int u=q.front();
q.pop();
in[u]=0;
for(auto it:e[u]){
int v=it.first;
ll w=it.second+h[v];
if(dis[v]<dis[u]+w){
dis[v]=dis[u]+w;
if(!in[v]){
q.push(v);
in[v]=1;
}
}
}
}
for(int j=1; j<=n; j++){
printf("%lld ",dis[j]);
}
printf("\n");
}
return 0;
}
反思总结
第一题做的没什么问题。
第二题花的时间太多了,在决策用记忆化搜索还是递推上不是很好,调试的时候也没想清楚细节,虽然最后是写对了,但是常数太大导致只拿了 60 分。
第三题是真的没思路,我写了个搜索,时间复杂度 \(\mathcal{O}(S^{r-l+1})\),一分没拿找。
第四题题都没读懂,给的时间太少了,没时间写部分分。
总结下来就是第二题花的时间太长了,导致第三题没有仔细想,第四题没读题。

浙公网安备 33010602011771号