第一题
#include<iostream>
using namespace std;
int n;
char c;
int main()
{
cin>>n>>c;
for(int i=1;i<=(n+1)/2;i++)//四舍五入
{
for(int j=1;j<=n;j++)
cout<<c;
cout<<"\n";
}
}
第二题 暴力枚举
L<1000 暴力枚举区间 0<K<10 数据范围开LL
#include<iostream>
using namespace std;
int len,k;
string s;
typedef long long ll;
bool check(int a,int b)
{
ll t=0;
for(int i=a;i<=b;i++)
t*=10,t+=(s[i]-'0');
if(t==0||t==1) return false;
for(int i=2;i*i<=t;i++)
if(t%i==0)
return false;
for(int i=a;i<=b;i++)
cout<<s[i];
return true;
}
int main()
{
cin>>len>>k>>s;
for(int i=0;i+k-1<len;i++)
{
int j=i+k-1;
if(check(i,j)) return 0;
}
cout<<404;
}
第三题 模拟
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=1e4+5;
struct person
{
int mon;//所有的钱数
int pos;//编号
int cnt;//抢到的个数
}per[N];
bool cmp(person a,person b)//收入降序,个数降序,编号增序
{
if(a.mon!=b.mon) return a.mon>b.mon;
if(a.cnt!=b.cnt) return a.cnt>b.cnt;
return a.pos<b.pos;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) per[i].pos=i;
for(int i=1;i<=n;i++)
{
int k;cin>>k;
for(int j=1;j<=k;j++)
{
int a,b;
cin>>a>>b;
per[i].mon-=b;//减去发红包的人
per[a].mon+=b;//加上收红包的人
per[a].cnt++;
}
}
sort(per+1,per+n+1,cmp);
for(int i=1;i<=n;i++)//输入数据以分为单位,输出结果以元为单位
cout<<per[i].pos<<" "<<fixed<<setprecision(2)<<per[i].mon*1.0/100<<endl;
}
第四题 单源最短路+堆优化
题意:森森从1号点走到n号点,点之间的边权有两种:现金和旅游金 ,每个点的都有一个现金兑换旅游金的汇率,且如果在某点换币,则必须将现金全部换为旅游金
问题可以转化为找一个中间点x,在1~x使用的现金,和在x点的汇率下,在x~n使用的旅游金对应的现金,两者之和的最小值
而在1~x使用的现金的最小值即为点1到点x以现金为边权的最短距离,在x~n使用的旅游金的最小值即为点n到点x以旅游金为边权的最短距离
看下数据范围 1≤n≤1e5,1≤m≤2e5 可以考虑 堆实现的dijkstra 复杂度为o(mlogn)
以现金为边权,正向建图,以1为起点做dijkstra;以旅游金为边权,反向建图,以n为起点做dijkstra,分别求中间点到两点的最短距离
接下来就有了第一版代码:
正反做完dijkstra后,在每次汇率变化后,枚举中间点,取结果的最小值
1≤n≤1e5 1≤q≤1e5 -> 枚举中间点的最坏情况 n*q<=1e10 超时
#include<bits/stdc++.h>
using namespace std;
int n,m,q;
typedef long long ll;
const long long INF=1e18;
typedef pair<ll,int> PII;
const int N=1e5+5,M=N*4;
int h1[N],h2[N],e[M],ne[M],idx;
ll w1[M],w2[M];
ll a[N];
ll dis1[N],dis2[N];
bool vis[N];
void add1(int a,int b,ll c)
{
e[idx]=b;
w1[idx]=c;
ne[idx]=h1[a];
h1[a]=idx++;
}
void add2(int a,int b,ll c)
{
e[idx]=b;
w2[idx]=c;
ne[idx]=h2[a];
h2[a]=idx++;
}
void d_1()//正向
{
memset(vis,false,sizeof vis);
dis1[1]=0;
priority_queue<PII,vector<PII>,greater<PII> >heap;
heap.push({0,1});
while(heap.size())
{
PII t=heap.top();heap.pop();
int ver=t.second;
ll distance=t.first;
if(vis[ver]) continue;
vis[ver]=true;
for(int i=h1[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(dis1[j]>distance+w1[i])
{
dis1[j]=distance+w1[i];
heap.push({dis1[j],j});
}
}
}
}
void d_2()//反向
{
memset(vis,false,sizeof vis);
dis2[n]=0;
priority_queue<PII,vector<PII>,greater<PII> >heap;
heap.push({0,n});
while(heap.size())
{
PII t=heap.top();heap.pop();
int ver=t.second;
ll distance=t.first;
if(vis[ver]) continue;
vis[ver]=true;
for(int i=h2[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(dis2[j]>distance+w2[i])
{
dis2[j]=distance+w2[i];
heap.push({dis2[j],j});
}
}
}
}
void solve()
{
int x;ll na;cin>>x>>na;
a[x]=na;
ll res=1e18;
for(int i=1;i<=n;i++)
{
if(dis1[i]==INF||dis2[i]==INF) continue;
ll t=dis1[i]+ceil(dis2[i]*1.0/a[i]);
res=min(res,t);
}
cout<<res<<endl;
}
int main()
{
cin>>n>>m>>q;
memset(h1,-1,sizeof h1);
memset(h2,-1,sizeof h2);
memset(dis1,0x3f,sizeof dis1);
memset(dis2,0x3f,sizeof dis2);
for(int i=1;i<=m;i++)
{
int u,v;ll c,d;cin>>u>>v>>c>>d;
add1(u,v,c),add2(v,u,d);
}
for(int i=1;i<=n;i++) cin>>a[i];
d_1();
d_2();
while(q--) solve();
}
考虑如何优化:
我们可以发现每次只变动一个点的汇率,也就是说所有点中只有一个点的花费是变化的,所以可以把所有点作为中间点的结果存起来,然后每次更新汇率变更的点的花费
可以考虑用STL的multiset容器,可以自动去重(更正:set可以去重,multiset不可以,两者均有序)和排序,操作时间复杂度为o(logn)
multiset<ll> st;
for(int i=1;i<=n;i++)
{
if(dis1[i]==INF||dis2[i]==INF) continue;
ll t=dis1[i]+ceil(dis2[i]*1.0/a[i]);
st.insert(t);
}
while(q--)
{
int x;ll na;cin>>x>>na;
if(dis1[x]!=INF&&dis2[x]!=INF)
{
st.erase(st.find(dis1[x]+ceil(dis2[x]*1.0/a[x])));
a[x]=na;
st.insert(dis1[x]+ceil(dis2[x]*1.0/a[x]));
}
cout<<*st.begin()<<endl;
}
在求完最短路后,对于其中和两边联通的点,存入其花费
对于每次更新,将对应原来位置的花费删除,并加入新的花费,最终取最小值即可
全部代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,q;
typedef long long ll;
const long long INF=0x3f3f3f3f3f3f3f3f;
typedef pair<ll,int> PII;
const int N=1e5+5,M=N*4;
int h1[N],h2[N],e[M],ne[M],idx;
ll w1[M],w2[M];
ll a[N];
ll dis1[N],dis2[N];
bool vis[N];
void add1(int a,int b,ll c)
{
e[idx]=b;
w1[idx]=c;
ne[idx]=h1[a];
h1[a]=idx++;
}
void add2(int a,int b,ll c)
{
e[idx]=b;
w2[idx]=c;
ne[idx]=h2[a];
h2[a]=idx++;
}
void d_1()
{
memset(vis,false,sizeof vis);
dis1[1]=0;
priority_queue<PII,vector<PII>,greater<PII> >heap;
heap.push({0,1});
while(heap.size())
{
PII t=heap.top();heap.pop();
int ver=t.second;
ll distance=t.first;
if(vis[ver]) continue;
vis[ver]=true;
for(int i=h1[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(dis1[j]>distance+w1[i])
{
dis1[j]=distance+w1[i];
heap.push({dis1[j],j});
}
}
}
}
void d_2()
{
memset(vis,false,sizeof vis);
dis2[n]=0;
priority_queue<PII,vector<PII>,greater<PII> >heap;
heap.push({0,n});
while(heap.size())
{
PII t=heap.top();heap.pop();
int ver=t.second;
ll distance=t.first;
if(vis[ver]) continue;
vis[ver]=true;
for(int i=h2[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(dis2[j]>distance+w2[i])
{
dis2[j]=distance+w2[i];
heap.push({dis2[j],j});
}
}
}
}
int main()
{
cin>>n>>m>>q;
memset(h1,-1,sizeof h1);
memset(h2,-1,sizeof h2);
memset(dis1,0x3f,sizeof dis1);
memset(dis2,0x3f,sizeof dis2);
for(int i=1;i<=m;i++)
{
int u,v;ll c,d;cin>>u>>v>>c>>d;
add1(u,v,c),add2(v,u,d);
}
for(int i=1;i<=n;i++) cin>>a[i];
d_1();
d_2();
multiset<ll> st;
for(int i=1;i<=n;i++)
{
if(dis1[i]==INF||dis2[i]==INF) continue;
ll t=dis1[i]+ceil(dis2[i]*1.0/a[i]);
st.insert(t);
}
while(q--)
{
int x;ll na;cin>>x>>na;
if(dis1[x]!=INF&&dis2[x]!=INF)
{
st.erase(st.find(dis1[x]+ceil(dis2[x]*1.0/a[x])));
a[x]=na;
st.insert(dis1[x]+ceil(dis2[x]*1.0/a[x]));
}
cout<<*st.begin()<<endl;
}
}
第五题 思维+递归
正序求计算结果,递归求偏导结果 ,先把每个节点的内容以及节点的关系记录,如何按正向顺序计算结果,反向顺序递归计算偏导结果
对于一个节点的定义
一个节点代表一个自变量或式子,储存自变量的值或者式子计算的值,以及该节点对应的操作符,无论求计算结果还是偏导结果,我们都需要知道这个节点是谁计算组合得来的,所以记录组成其的0/1/2个节点
另外,这道题比较坑的地方就是没有说输入数据是按顺序给的ヽ(#`Д´)ノ,所以我们还需要判断出哪个节点是最后的式子,所以加一个标记ne,表示其是否存在后继节点,若不存在则为最后的式子
struct Node
{
double w;//值
int op;//操作符
int front[4];//前面的0/1/2个节点
bool ne;
}node[N];
记得我们最开始说输入顺序是不定的,所以我们先储存节点,后排序,同时初始化节点信息
for(int i=0;i<n;i++)
{
int op;scanf("%d",&op);
node[i].op=op;
if(op==0)
{
double x;scanf("%lf",&x);
node[i].w=x;
node[i].front[1]=node[i].front[2]=-1;
vc.push_back(i);
}
else if(op==1)
{
int x1,x2;scanf("%d%d",&x1,&x2);
node[i].front[1]=x1;
node[i].front[2]=x2;
node[x1].ne=true;
node[x2].ne=true;
}
else if(op==2)
{
int x1,x2;scanf("%d%d",&x1,&x2);
node[i].front[1]=x1;
node[i].front[2]=x2;
node[x1].ne=true;
node[x2].ne=true;
}
else if(op==3)
{
int x1,x2;scanf("%d%d",&x1,&x2);
node[i].front[1]=x1;
node[i].front[2]=x2;
node[x1].ne=true;
node[x2].ne=true;
}
else if(op==4)
{
int x;scanf("%d",&x);
node[i].front[1]=x;
node[i].front[2]=-1;
node[x].ne=true;
}
else if(op==5)
{
int x;scanf("%d",&x);
node[i].front[1]=x;
node[i].front[2]=-1;
node[x].ne=true;
}
else if(op==6)
{
int x;scanf("%d",&x);
node[i].front[1]=x;
node[i].front[2]=-1;
node[x].ne=true;
}
}
如何我们观察一下应该如何找顺序
类似于BFS的写法,从最终的式子节点开始,对于一个节点,先拓展其前面的1/2个节点,并有序存储,注意不要重复记录
(此处有一点:拓展时必须先拓展其第二个节点,即如果为x+y时必须先写y,要不会错一个测试数据。。具体原因我也不知道,参考网上代码改对的这一测试点˚‧º·(˚ ˃̣̣̥᷄⌓˂̣̣̥᷅ )‧º·˚)
int t=-1;
for(int i=0;i<n;i++)
if(!node[i].ne)
{
t=i;
break;
}
q.push(t);
v.push_back(t);
while(!q.empty())
{
int top=q.front();q.pop();
if(node[top].front[2]!=-1)
{
q.push(node[top].front[2]);
if(!vis[node[top].front[2]])
{
vis[node[top].front[2]]=true;
v.push_back(node[top].front[2]);
}
}
if(node[top].front[1]!=-1)
{
q.push(node[top].front[1]);
if(!vis[node[top].front[1]])
{
vis[node[top].front[1]]=true;
v.push_back(node[top].front[1]);
}
}
}
按从自变量开始的顺序进行正向计算,模拟即可
for(int i=n-1;i>=0;i--)
{
int u=v[i];
int op=node[u].op;
if(op==0) continue;
else if(op==1)
{
int x1=node[u].front[1],x2=node[u].front[2];
node[u].w=node[x1].w+node[x2].w;
}
else if(op==2)
{
int x1=node[u].front[1],x2=node[u].front[2];
node[u].w=node[x1].w-node[x2].w;
}
else if(op==3)
{
int x1=node[u].front[1],x2=node[u].front[2];
node[u].w=node[x1].w*node[x2].w;
}
else if(op==4)
{
int x=node[u].front[1];
node[u].w=exp(node[x].w);
}
else if(op==5)
{
int x=node[u].front[1];
node[u].w=log(node[x].w);
}
else if(op==6)
{
int x=node[u].front[1];
node[u].w=sin(node[x].w);
}
}
从最终式子开始,递归求偏导值
double dfs(int u,int q)
{
int op=node[u].op;
if(op==0)
{
if(u==q) return 1;
else return 0;
}
else if(op==1)
{
int x1=node[u].front[1],x2=node[u].front[2];
double tr=dfs(x1,q)+dfs(x2,q);
return tr;
}
else if(op==2)
{
int x1=node[u].front[1],x2=node[u].front[2];
double tr=dfs(x1,q)-dfs(x2,q);
return tr;
}
else if(op==3)
{
int x1=node[u].front[1],x2=node[u].front[2];
double tr1=dfs(x1,q),tr2=dfs(x2,q);
double tres=node[x1].w*tr2+node[x2].w*tr1;
return tres;
}
else if(op==4)
{
int x=node[u].front[1];
double tr=dfs(x,q);
double tres=exp(node[x].w)*tr;
return tres;
}
else if(op==5)
{
int x=node[u].front[1];
double tr=dfs(x,q);
if(tr==0) return 0;
double tres=(1.0/node[x].w)*tr;
return tres;
}
else if(op==6)
{
int x=node[u].front[1];
double tr=dfs(x,q);
double tres=cos(node[x].w)*tr;
return tres;
}
}
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
int n;
struct Node
{
double w;//值
int op;//操作符
int front[4];//前面的0/1/2个节点
bool ne;
}node[N];
vector<int> vc;//自变量
queue<int> q;//层次遍历对节点排序
vector<int> v;//倒序存放节点
bool vis[N];
double dfs(int u,int q)
{
int op=node[u].op;
if(op==0)
{
if(u==q) return 1;
else return 0;
}
else if(op==1)
{
int x1=node[u].front[1],x2=node[u].front[2];
double tr=dfs(x1,q)+dfs(x2,q);
return tr;
}
else if(op==2)
{
int x1=node[u].front[1],x2=node[u].front[2];
double tr=dfs(x1,q)-dfs(x2,q);
return tr;
}
else if(op==3)
{
int x1=node[u].front[1],x2=node[u].front[2];
double tr1=dfs(x1,q),tr2=dfs(x2,q);
double tres=node[x1].w*tr2+node[x2].w*tr1;
return tres;
}
else if(op==4)
{
int x=node[u].front[1];
double tr=dfs(x,q);
double tres=exp(node[x].w)*tr;
return tres;
}
else if(op==5)
{
int x=node[u].front[1];
double tr=dfs(x,q);
if(tr==0) return 0;
double tres=(1.0/node[x].w)*tr;
return tres;
}
else if(op==6)
{
int x=node[u].front[1];
double tr=dfs(x,q);
double tres=cos(node[x].w)*tr;
return tres;
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int op;scanf("%d",&op);
node[i].op=op;
if(op==0)
{
double x;scanf("%lf",&x);
node[i].w=x;
node[i].front[1]=node[i].front[2]=-1;
vc.push_back(i);
}
else if(op==1)
{
int x1,x2;scanf("%d%d",&x1,&x2);
node[i].front[1]=x1;
node[i].front[2]=x2;
node[x1].ne=true;
node[x2].ne=true;
}
else if(op==2)
{
int x1,x2;scanf("%d%d",&x1,&x2);
node[i].front[1]=x1;
node[i].front[2]=x2;
node[x1].ne=true;
node[x2].ne=true;
}
else if(op==3)
{
int x1,x2;scanf("%d%d",&x1,&x2);
node[i].front[1]=x1;
node[i].front[2]=x2;
node[x1].ne=true;
node[x2].ne=true;
}
else if(op==4)
{
int x;scanf("%d",&x);
node[i].front[1]=x;
node[i].front[2]=-1;
node[x].ne=true;
}
else if(op==5)
{
int x;scanf("%d",&x);
node[i].front[1]=x;
node[i].front[2]=-1;
node[x].ne=true;
}
else if(op==6)
{
int x;scanf("%d",&x);
node[i].front[1]=x;
node[i].front[2]=-1;
node[x].ne=true;
}
}
int t=-1;
for(int i=0;i<n;i++)
if(!node[i].ne)
{
t=i;
break;
}
q.push(t);
v.push_back(t);
while(!q.empty())
{
int top=q.front();q.pop();
if(node[top].front[2]!=-1)
{
q.push(node[top].front[2]);
if(!vis[node[top].front[2]])
{
vis[node[top].front[2]]=true;
v.push_back(node[top].front[2]);
}
}
if(node[top].front[1]!=-1)
{
q.push(node[top].front[1]);
if(!vis[node[top].front[1]])
{
vis[node[top].front[1]]=true;
v.push_back(node[top].front[1]);
}
}
}
for(int i=n-1;i>=0;i--)
{
int u=v[i];
int op=node[u].op;
if(op==0) continue;
else if(op==1)
{
int x1=node[u].front[1],x2=node[u].front[2];
node[u].w=node[x1].w+node[x2].w;
}
else if(op==2)
{
int x1=node[u].front[1],x2=node[u].front[2];
node[u].w=node[x1].w-node[x2].w;
}
else if(op==3)
{
int x1=node[u].front[1],x2=node[u].front[2];
node[u].w=node[x1].w*node[x2].w;
}
else if(op==4)
{
int x=node[u].front[1];
node[u].w=exp(node[x].w);
}
else if(op==5)
{
int x=node[u].front[1];
node[u].w=log(node[x].w);
}
else if(op==6)
{
int x=node[u].front[1];
node[u].w=sin(node[x].w);
}
}
printf("%.3lf\n",node[v[0]].w*1.0);
for(int i=0;i<vc.size();i++)
{
printf("%.3lf",dfs(t,vc[i])*1.0);
if(i!=vc.size()-1) printf(" ");
}
}