cf933A dp题
一开始看错是连续子序列了,然后样例刚好能过。。
然后正解没想出来,网上看了题解:感觉正解是枚举2开始的位置,然后再枚举翻转的区间,pos左右两侧分别求出贡献最大的那个区间,左右两部分的贡献是独立计算的
#include <cstdio> #include <cmath> #include <queue> #include <cstring> #include <algorithm> using namespace std; #define mst(a,b) memset((a),(b),sizeof(a)) #define rush() int T;scanf("%d",&T);while(T--) typedef long long ll; const int maxn = 2005; const ll mod = 1e9+7; const ll INF = 1e18+5; const double eps = 1e-9; int n; int a[maxn]; int pre1[maxn]; int pre2[maxn]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); pre1[i]=pre1[i-1]+(a[i]==1); } for(int i=n;i>=1;i--) pre2[i]=pre2[i+1]+(a[i]==2); int ans=0; for(int k=1;k<=n+1;k++) //枚举位置pos { int num1=0,num2=0; for(int i=1;i<=k;i++) num1=max(num1,pre1[i-1]+pre2[i]-pre2[k]); //枚举l for(int i=k;i<=n+1;i++) num2=max(num2,pre2[i]+pre1[i-1]-pre1[k-1]); //枚举r ans=max(ans,num1+num2); } printf("%d ",ans); }
然后是神仙做法,因为答案必定是11111222221111122222的前缀样式,所以只要从左到右扫一次即可。。
#include<bits/stdc++.h> using namespace std; #define maxn 2000 int n; int a[maxn+5]; int f[maxn+5][5]; int main() { scanf("%d",&n); for(int i=1; i<=n; i++) scanf("%d",&a[i]); for(int i=1; i<=n; i++) { for(int j=1; j<=4; j++) { f[i][j]=f[i][j-1]; if(a[i]==(j+1)%2+1) f[i][j]=max(f[i][j],f[i-1][j]+1); else f[i][j]=max(f[i][j],f[i-1][j]); } } printf("%d",f[n][4]); return 0; }
cf938D 最短路+新建源点
第一次在cf上做这种题,这题就是把每个点权转化为到源点的边权,然后跑一次最短路即可
/* 给定无向图,有边权和点权 现在要求出每个点i的最小代价2*d(i,j)+aj,i可以等于j 建图:边权*2,建立源点和每个点连边,边权是ai 然后以源点为起点跑最短路即可 */ #include<bits/stdc++.h> #include<queue> using namespace std; #define maxn 400005 #define ll long long struct Edge{ll to,nxt,w;}edge[maxn<<2]; ll head[maxn],tot,n,m,a[maxn]; void init(){ memset(head,-1,sizeof head); tot=0; } void addedge(ll u,ll v,ll w){ edge[tot].to=v;edge[tot].w=w; edge[tot].nxt=head[u];head[u]=tot++; } ll d[maxn],v[maxn]; priority_queue<pair<ll,ll> >pq; void dijkstra(){ memset(d,0x3f,sizeof d); memset(v,0,sizeof v); d[n+1]=0; pq.push(make_pair(0,n+1)); while(pq.size()){ pair<ll,ll>c=pq.top();pq.pop(); if(v[c.second])continue; v[c.second]=1; ll x=c.second; for(int i=head[x];i!=-1;i=edge[i].nxt){ ll y=edge[i].to,w=edge[i].w; if(v[y])continue; if(d[y]>d[x]+w){ d[y]=d[x]+w; pq.push(make_pair(-d[y],y)); } } } } int main(){ init(); cin>>n>>m; for(int i=1;i<=m;i++){ ll u,v,w; cin>>u>>v>>w; addedge(u,v,2*w); addedge(v,u,2*w); } for(int i=1;i<=n;i++){ cin>>a[i]; addedge(i,n+1,a[i]); addedge(n+1,i,a[i]); } dijkstra(); for(int i=1;i<=n;i++) cout<<d[i]<<" "; }
cf939E 三分+简单公式
学了下三分的写法,能用来求单峰函数的极值
三分链接: https://blog.csdn.net/pi9nc/article/details/9666627
/* 1.往多重集合s里加数 ,每次给的数时单调不递减的 2.求子集ss,使max(ss)-mean(ss)最大 三分法来做即可 */ #include<bits/stdc++.h> using namespace std; #define maxn 500005 #define ll long long double sum[maxn],n; ll len,Max; double f(int i){ return ((double)sum[i]+Max)/(i+1); } double sanfen(int l,int r){ while(l<r-1){ int mid=(l+r)>>1; int mmid=(mid+r)>>1; if(f(mid)>f(mmid)) l=mid; else r=mmid; } if(f(l)>f(r))return f(r); return f(l); } int main(){ int q,op; cin>>q; while(q--){ cin>>op; if(op==1){ cin>>Max; sum[++len]=Max+sum[len-1]; } else { printf("%.10lf ",Max-sanfen(1,len-1)); } } }
cf935D 概率递推dp
还是第一次做这种题。。要学一下概率dp了。。
/* 概率递推+逆元取模运算 从末尾往前推比从前往后推要更简单 */ #include<bits/stdc++.h> using namespace std; #define ll long long #define mod 1000000007 #define maxn 100005 ll a[maxn],b[maxn],n,m,dp[maxn]; ll inv2,invm; ll exgcd(ll a,ll b,ll &x,ll &y){ if(b==0){x=1,y=0;return a;} ll d=exgcd(b,a%b,x,y); ll z=x;x=y;y=z-a/b*x; return d; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)cin>>b[i]; ll x,y; exgcd(2,mod,x,y);inv2=(x+mod)%mod; exgcd(m,mod,x,y);invm=(x+mod)%mod; for(int i=n;i>=1;i--){ if(a[i] && b[i]){//第i位都是固定的数 if(a[i]==b[i])dp[i]=dp[i+1]; else dp[i]=a[i]>b[i]; } else if(b[i])//ai填数,ai==bi的概率+ai>bi的概率 dp[i]=dp[i+1]*invm%mod+(m-b[i])*invm%mod; else if(a[i])//bi填数,ai==bi的概率+ai>bi的概率 dp[i]=dp[i+1]*invm%mod+(a[i]-1)*invm%mod; else if(!a[i] && !b[i])//都填数,ai==bi的概率+ai!=bi且ai>bi的概率 dp[i]=dp[i+1]*invm%mod+(m-1)*inv2%mod*invm%mod; } cout<<dp[1]%mod<<endl; }