显然貌似是某其他学校考过的,而我们的题竟然其他学校也在考,233,教练们是什么py(thon)交易
显然作为NOIP选手考这样一套NOIP难度的题目,考成这样确实很差,尤其是t2的思想错误,竟然认为最小树形图和最小生成树是一样的,我竟然tarjan之后在用最小生成树来做???真是羞耻啊…t1根本不会,期望以前根本没做过,现在总算是有一些起色,t3写的n方暴力,那么现在我们就来看题吧
t1是一个期望dp,我们可以从后面往前推,现状压一下,表示现在是这个集合到买满所有的东西的集合的期望(平均)次数。显然我们可以得到dp[(1<<(n-1))-1]是0,因为此时本来就已经买完了,不需要再买了。
然后现在我们来推dp转移方程
f[s]=∑f[s′]∗P(s−>s′)+f[s]∗P(s−>s)+1
f[s](1−P(s−>s))=∑f[s′]∗P(s−>s′)+1
f[s]=(∑f[s′]∗P[s−>s′]+1)/(1−P(s−>s));
解释一下上面的推导,其实已经写得非常清楚啦,就是说从所有可以转移到的新的集合转移过来,让他们的期望全部乘以概率,然后再加上自己rand到已经有的数的概率乘期望,然后自然要+1,因为我们无论是如何转移,从哪里转移,最后自己都还要走一步走到那个位置去。
然后s->s’表示转移到新集合的概率,s->s表示转移到自己的概率,然后dp是不能有环的,是一个DAG转移,所以我们移项,得到第二步的式子,最后变成第三步的式子,就可以做了,愉快地转移,看代码瞬间懂。
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
const int MAXN=2000000+10;
double dp[MAXN],P[MAXN];
int n,m;
long long ans;
int main(){
freopen("gift.in","r",stdin);
freopen("gift.out","w",stdout);
int tmp;
scanf("%d",&n);
for(register int i=1;i<=n;i++){
scanf("%lf%d",&P[i],&tmp);
if(P[i]>0) ans+=tmp;
}
m=(1<<n)-1;dp[m]=0;
for(register int s=m-1;s>=0;s--){
double sum=0;
for(int j=0;j<n;j++){
if((s&(1<<j))==0){
sum+=P[j+1];
int ss=s|(1<<j);
dp[s]+=P[j+1]*dp[ss];
}
}
dp[s]++;
dp[s]=dp[s]/sum;
}
printf("%I64d\n%.3lf\n",ans,dp[0]);
return 0;
}
t2的话,显然直接tarjan一下然后贪心就可以了,贪心策略是对于每一个scc,我们选取其入边里面权值最小的边来进行连接,因为0号节点已经是树根了,所以直接这样贪心策略完全没有问题。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100000*2;
int dfn[MAXN],head[MAXN],tail,low[MAXN],sta[MAXN],top,timer,scc[MAXN],cnt,fa[MAXN],N,M,ff,tt,fl;
int minn[MAXN];
bool vis[MAXN];
struct Line{
int from,to,nxt,flow;
}line[MAXN];
struct Edge{
int from,to,flow;
}edge[MAXN];
void add_line(int from,int to,int flow){
line[++tail].from=from;
line[tail].nxt=head[from];
line[tail].to=to;
line[tail].flow=flow;
head[from]=tail;
}
void add_edge(int from,int to,int flow){
edge[++tail].from=from;
edge[tail].to=to;
edge[tail].flow=flow;
}
bool cmp(const Edge& A,const Edge& B){return A.flow<B.flow;}
void tarjan(int u){
dfn[u]=++timer;
low[u]=dfn[u];
vis[u]=true;
sta[++top]=u;
for(register int i=head[u];i;i=line[i].nxt){
int v=line[i].to;
if(dfn[v]==0){
tarjan(v);low[u]=min(low[u],low[v]);
}else if(vis[v]==true){
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
scc[u]=++cnt;
vis[u]=false;
while(sta[top]!=u){
scc[sta[top]]=cnt;
vis[sta[top]]=false;
top--;
}
top--;
}
}
int main(){
freopen("message.in","r",stdin);
freopen("message.out","w",stdout);
while(scanf("%d%d",&N,&M)!=EOF&&(!(N==0&&M==0))){
memset(head,0,sizeof(head));tail=0;
memset(dfn,0,sizeof(dfn));cnt=0;
memset(minn,63,sizeof(minn));
for(register int i=1;i<=M;i++){
scanf("%d%d%d",&ff,&tt,&fl);ff++;tt++;
add_line(ff,tt,fl);
}
for(register int i=1;i<=N;i++){
if(dfn[i]==0) tarjan(i);
}
tail=0;
for(register int i=1;i<=M;i++){
int u=line[i].from,v=line[i].to;
if(scc[u]==scc[v]) continue;
add_edge(scc[u],scc[v],line[i].flow);
}
for(register int i=1;i<=tail;i++){
int u=edge[i].from,v=edge[i].to;
minn[v]=min(minn[v],edge[i].flow);
}
int rt=scc[1];int ass=0;
for(register int i=1;i<=cnt;i++){
if(i==rt) continue;
ass+=minn[i];
}
printf("%d\n",ass);
}
return 0;
}
t3不说了,现在都还不会,会了补上,我贴个跑了一百秒的n方大暴力
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;
struct Point{int x,y;}p[MAXN];
int n,start,end,maxn[20][100000],minn[20][100000];
bool cmp(const Point& A,const Point& B){return A.x<B.x;}
void build(){
for(register int i=1;i<=n;i++) maxn[0][i]=minn[0][i]=p[i].y;
for(register int j=1;(1<<j)<=n;j++)
for(register int i=1;i+(1<<j)-1<=n;i++)
maxn[j][i]=max(maxn[j-1][i],maxn[j-1][i+(1<<(j-1))]),minn[j][i]=min(minn[j-1][i],minn[j-1][i+(1<<(j-1))]);
}
int query_max(int L,int R,int k){
return max(maxn[k][L],maxn[k][R-(1<<k)+1]);
}
int query_min(int L,int R,int k){
return min(minn[k][L],minn[k][R-(1<<k)+1]);
}
int main(){
freopen("raid.in","r",stdin);
freopen("raid.out","w",stdout);
scanf("%d",&n);
for(register int i=1;i<=n;i++){scanf("%d%d",&p[i].x,&p[i].y);}
sort(p+1,p+n+1,cmp);
build();int cnt=0;
for(register int k=2;k<=n;k++){
int kk=0;
while((1<<(kk+1))<=k) kk++;
for(register int i=1;i<=n;i++){
if(i+k-1>n) break;
int maks=query_max(i,i+k-1,kk),miks=query_min(i,i+k-1,kk);
if(maks==miks+k-1) cnt++;
}
}
printf("%d\n",cnt+n);
return 0;
}