emmmm
又爆蛋了QAQ
真TM难搞~~~
分析题叭,争取考后要明明白白的AK
T1 礼物(gift)
其实考试的时候没有看数据范围,只是觉得很简单但是没有思路
然额,我没有想出来正解,10%的分也没拿到
对于10%的数据,N = 1
那么只输出$1/p_i$就行了
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; int a,ans; double b,ans2; int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>b>>a; ans+=a; ans2=1.0/b; } cout<<ans<<'\n'; printf("%.3lf",ans2); return 0; }
这就有10分了,我哭辽~~~
下面说正解状压dp
dp[i]保存的是当前状态,表示,当前购买次数距离最终状态剩下的次数,所以答案在dp[0]中保存
因为有n个物品,所以一共有1<<(n-1)个状态
用二进制保存的话,dp[1<<(n-1)]=0
后面的由此递推
转移方程的话,推理过程如下:
s是当前状态,s'是前一个状态
$ dp[s]=\sum dp[s']*p[i]+(1-\sum p[i])*dp[s]+1 $
要高斯消元求解,但是我又不会QAQ
那么移项之后,发现了不得了的事情,s'就比s少1而已
$ \sum p[i]*dp[s]=\sum dp[s']*p[i]+1 $
再移项,求出dp[i]的转移方程
$ dp[s]=(\sum dp[s']*p[i]+1)/ \sum p[i] $
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #define int long long using namespace std; int n,b; int maxn; double a,ans; double dp[1<<20]; double p[21]; signed main(){ cin>>n; for(int i=1;i<=n;i++){ scanf("%lf",&p[i]); cin>>b; maxn+=b; } for(int i=(1<<n)-2;i>=0;i--){ double sum=0.0; for(int j=1;j<=n;j++){ if(i&(1<<(j-1))) continue;//表示第j个物品已经购买 dp[i]+=p[j]*dp[i|(1<<(j-1))];//转移方程 sum+=p[j]; } dp[i]=(dp[i]+1.0)/sum; } cout<<maxn<<'\n'; printf("%0.3lf",dp[0]); return 0; }
此题完结,简直噩梦。
T2 通讯(message)
这题挺水的,虽然我没A
意思就是在同一个强联通分量里的点代价都是0的
然后去维护一个强联通分量向其他的点出去代价最小值
清空数组和变量u就完了,我竟然写了SPFA
我WA的一声哭了出来
哦对了,在建图的时候要u++,v++
因为i题里是0开始存图的
#include<bits/stdc++.h> using namespace std; struct rec { int nxt; int to; int w; }e[100010]; int n,m; int head[50010],cnt; int dfn[50010],low[50010],sta[50010],ins[50010],c[50010],num,top,tot; int ans; int fl[50010]; void pre_work() { cnt=num=top=tot=0; ans=0; for(int i=1;i<=n;i++) head[i]=dfn[i]=low[i]=ins[i]=c[i]=0; memset(fl,0x3f,sizeof(fl)); } void add(int x,int y,int w) { e[++cnt].nxt=head[x]; e[cnt].to=y; e[cnt].w=w; head[x]=cnt; } void tarjan(int x) { dfn[x]=low[x]=++num; sta[++top]=x; ins[x]=1; for(int i=head[x];i;i=e[i].nxt) { if(!dfn[e[i].to]) { tarjan(e[i].to); low[x]=min(low[x],low[e[i].to]); } else if(ins[e[i].to]) low[x]=min(low[x],dfn[e[i].to]); } if(dfn[x]==low[x]) { tot++; int y; do { y=sta[top--]; ins[y]=0; c[y]=tot; }while(x!=y); } } int main(){ memset(fl,0x3f,sizeof(fl)); while(cin>>n>>m && n && m){ for(int i=1;i<=m;i++){ int u,v,w; cin>>u>>v>>w; u++,v++; add(u,v,w); } tarjan(1); for(int x=1;x<=n;x++) for(int i=head[x];i;i=e[i].nxt) if(c[x]!=c[e[i].to]) fl[c[e[i].to]]=min(fl[c[e[i].to]],e[i].w); fl[c[1]]=0; for(int i=1;i<=tot;i++)ans+=fl[i]; printf("%d\n",ans); pre_work(); } return 0; }
T3 奇袭(raid)
对于30%,可以用前缀和水到27分
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=5555; int map[maxn][maxn]; int n; int ans; int main(){ cin>>n; for(int i=1;i<=n;i++){ int u,v; cin>>u>>v; map[u][v]=1; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) map[i][j]=map[i][j]+map[i-1][j]+map[i][j-1]-map[i-1][j-1]; ans=n; for(int k=2;k<=n;k++) for(int i=0;i<=n-k;i++) for(int j=0;j<=n-k;j++) if(map[i][j]+map[i+k][j+k]-map[i+k][j]-map[i][j+k]==k) ans++; cout<<ans<<endl; return 0; }
前缀和是$ n^3 $的,然后我们考虑$ n^2 $的做法
ST表91分
#include<bits/stdc++.h> using namespace std; const int L(1<<20|1); int n; int maxn[50001][20],minn[50001][20]; int ans; int flag[50001],lgr[50001]; int lgn; void st(int x){ for(int i=1;i<=lgn;i++) for(int j=1;j+(1<<i)<=x+1;j++){ maxn[j][i]=max(maxn[j][i-1],maxn[j+(1<<(i-1))][i-1]); minn[j][i]=min(minn[j][i-1],minn[j+(1<<(i-1))][i-1]); } } pair<int,int> query(int l,int r){ int k=lgr[r-l+1]; return make_pair(max(maxn[l][k],maxn[r-(1<<k)+1][k]),min(minn[l][k],minn[r-(1<<k)+1][k])); } int main(){ memset(minn,0x3f,sizeof(minn)); cin>>n; lgn=log2(n); for(int i=1;i<=n;i++){ int x,y; cin>>x>>y; maxn[x][0]=minn[x][0]=y; flag[y]=x; lgr[i]=log2(i); } st(n); for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++){ //if(flag[maxn[j][0]-1]<i&&flag[maxn[j][0]+1]<i)break;//没有这个剪枝就64,添上就有91 pair<int,int> flag=query(i,j); if(flag.first-flag.second==j-i)ans++; } cout<<ans+n<<endl; return 0; }