NOI 2012
[NOI2012]随机数生成器
矩阵乘法,要注意两数相乘时,可能会爆long long,用快速乘。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; long long mod1,a,c,x,n,mod2; long long mul(long long p,long long b) { long long res=0; for(;b;b>>=1) { if(b&1) res=(res+p)%mod1; p=(p+p)%mod1; } return res; } struct Matrix { long long a[10][10]; Matrix(){memset(a,0,sizeof(a));} Matrix operator * (const Matrix &aa) { Matrix c; for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) for(int p=1;p<=9;p++) c.a[i][j]=(c.a[i][j]%mod1+mul((a[i][p]%mod1),(aa.a[p][j]%mod1))%mod1)%mod1; return c; } }; Matrix Pow(Matrix p,long long b) { Matrix res; for(int i=1;i<=3;i++) res.a[i][i]=1LL; for(;b;b>>=1) { if(b&1) res=res*p; p=p*p; } return res; } int main() { scanf("%lld%lld%lld%lld%lld%lld",&mod1,&a,&c,&x,&n,&mod2); Matrix trans,ini; trans.a[1][1]=0,trans.a[1][2]=0,trans.a[1][3]=0; trans.a[2][1]=1,trans.a[2][2]=a%mod1,trans.a[2][3]=0; trans.a[3][1]=0,trans.a[3][2]=1,trans.a[3][3]=1; ini.a[1][1]=0,ini.a[1][2]=x,ini.a[1][3]=c%mod1; Matrix ans=ini*Pow(trans,n); printf("%lld",(ans.a[1][2]%mod2+mod2)%mod2); return 0; }
[NOI2012]美食节
本来应该这样建图,把每个厨师拆成sigma_p个点,分别表示倒数第i次做菜的该位厨师j,向每个菜品p连边。
但是这样会TLE,我们不妨模拟一下MCMF的过程,对于每个厨师来说,首先SPFA出来的代价最小的通路肯定是最后一次做某菜,因为等待的时间短。我们不妨先建所有的倒数第一个点,哪个满流了就建倒数第二个点,很好的模拟了增广的过程,剪掉了许多不必要的边。
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int maxm=1e7+10; const int maxn=1e5+10; #define idd(i,j) ((i-1)*sum+j+n) #define left(x) ((x-n-1)/sum+1) #define right(x) ((x-n-1)%sum+1) struct point { int to,nxt; int cap,cost; }edge[maxm]; int n,m,Mincost,Maxflow,tot,S,T,sum; int head[maxn],p[maxn],flow[maxn],vis[maxn],id[maxn],pre[maxn],dis[maxn]; int ti[120][120]; inline void add(int u,int v,int c,int f) { edge[tot]=(point){v,head[u],c,f}; head[u]=tot++; edge[tot]=(point){u,head[v],0,-f}; head[v]=tot++; } inline bool Bfs() { queue<int> q; memset(dis,-1,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[S]=0; flow[S]=1e9; vis[S]=1; q.push(S); while(!q.empty()) { int tt=q.front(); q.pop(); vis[tt]=0; for(int i=head[tt];~i;i=edge[i].nxt) { int v=edge[i].to; if(edge[i].cap && (dis[v]==-1 || dis[v]>dis[tt]+edge[i].cost)) { dis[v]=dis[tt]+edge[i].cost; flow[v]=min(edge[i].cap,flow[tt]); id[v]=i; pre[v]=tt; if(!vis[v]) vis[v]=1,q.push(v); } } } return dis[T]!=-1; } inline void Mcmf() { while(Bfs()) { int tmp=T,ad=0; while(tmp!=S) { edge[id[tmp]].cap-=flow[T]; edge[id[tmp]^1].cap+=flow[T]; tmp=pre[tmp]; } Mincost+=flow[T]*dis[T]; ad=pre[T]+1; add(ad,T,1,0); for(int i=1;i<=n;i++) add(i,ad,1,ti[i][left(ad)]*right(ad)); } } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&p[i]),sum+=p[i]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&ti[i][j]); S=0,T=sum*m+n+1; for(int i=1;i<=n;i++) add(S,i,p[i],0); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) add(i,idd(j,1),1,ti[i][j]); for(int i=1;i<=m;i++) add(idd(i,1),T,1,0); Mcmf(); printf("%d",Mincost); return 0; }
[NOI2012]迷失游乐园
当只有树的时候很套路,up[x]表示从节点x向上走的期望长度,down[x]表示从节点x向下走的期望长度。
当是基环树的时候,先算down[x],每棵树的树根的up[x]要注意一下,左右全部跑一遍,对于每个环内的点都有可能继续走或下到树里面。
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=1e5+10; struct point { int to; int nxt; double w; }edge[maxn*2]; int n,m,tot,cnt,flag; double up[maxn],down[maxn],w_fa[maxn],W[33][33]; int hav[maxn],nson[maxn],head[maxn],nn[33],in[maxn],vis[maxn],hash[maxn]; int fath[maxn],pre[maxn],nex[maxn]; inline void add(int u,int v,double w) { tot++; edge[tot].nxt=head[u]; edge[tot].to=v; edge[tot].w=w; head[u]=tot; } inline void dfs1(int x,int fa) { for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(vis[v] || v==fa) continue; nson[x]++,w_fa[v]=edge[i].w; hav[v]=1; dfs1(v,x); down[x]+=down[v]+(double)edge[i].w; } if(nson[x]) down[x]/=(double)nson[x]; } inline void dfs2(int x,int fa) { if(fa) { if(nson[fa]-1+hav[fa]>0) up[x]=(double)(down[fa]*nson[fa]-w_fa[x]-down[x]+hav[fa]*up[fa])/(double)(nson[fa]-1+hav[fa]); up[x]+=(double)w_fa[x]; } for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(vis[v] || v==fa) continue; dfs2(v,x); } } inline void Tree_solve() { dfs1(1,0); dfs2(1,0); } inline void get_cir(int x,int fa) { vis[x]=1; for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(v==fa) continue; if(vis[v]){flag=v; return;} get_cir(v,x); if(flag>0){if(flag==x) flag=-1; return;} if(flag==-1) break; } vis[x]=0; } void find_cir(int x, int f) { if(hash[x]) return; nn[++cnt] = x; hash[x] = cnt; hav[x]=2; for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if (v == f) continue; if (!vis[v]) continue; pre[v] = x; nex[x] = v; find_cir(v, x); W[hash[x]][hash[v]] = W[hash[v]][hash[x]] = edge[i].w; break; } } inline void Cir_Tree_solve() { for(int i=1;i<=n;i++) if(vis[i]) { find_cir(i,0); break; } double nowp; memset(w_fa,0,sizeof(w_fa)); for(int i=1;i<=cnt;i++) dfs1(nn[i],0); for (int i=1;i<=cnt;i++) { double k=1.0; int u=nn[i]; for(int j=nex[u];j!=u;j=nex[j]) { if(nex[j]!=u) up[u]+=(double)k*((down[j]*nson[j])/(double)(nson[j]+1)+W[hash[pre[j]]][hash[j]]); else up[u]+=k*(down[j]+W[hash[pre[j]]][hash[j]]); k/=(nson[j]+1); } k=1.0; for(int j=pre[u];j!=u;j=pre[j]) { if(pre[j]!=u) up[u]+=(double)k*((down[j]*nson[j])/(double)(nson[j]+1)+W[hash[nex[j]]][hash[j]]); else up[u]+=k*(down[j]+W[hash[nex[j]]][hash[j]]); k/=(nson[j]+1); } up[u]/=2.0; } for(int i=1;i<=cnt;i++) dfs2(nn[i],0); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; double w; scanf("%d%d%lf",&u,&v,&w); add(u,v,w),add(v,u,w); } get_cir(1,0); if(m<=n-1) Tree_solve(); else Cir_Tree_solve(); double ans=0.0; for(int i=1;i<=n;i++) ans=ans+(up[i]*hav[i]+down[i]*nson[i])/(double)(nson[i]+hav[i]); ans/=(double)n; printf("%.5lf",ans); return 0; }
[NOI2012]骑行川藏
讲的很清楚。
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define eps 1e-12 const int maxn=1e4+10; const int inf=1e9; int n; double E,s[maxn],k[maxn],v[maxn],x[maxn]; inline double cal(double lam) { double tmp=0; for(int i=1;i<=n;i++) { double l=max(0.0,v[i]),r=inf; while(l+eps<r) { double mid=(l+r)*0.5; if(2*lam*k[i]*mid*mid*(mid-v[i])>1) r=mid; else l=mid; } x[i]=(l+r)*0.5; tmp+=k[i]*(x[i]-v[i])*(x[i]-v[i])*s[i]; } return tmp; } int main() { scanf("%d%lf",&n,&E); for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&s[i],&k[i],&v[i]); double l=0,r=inf; while(l+eps<r) { double mid=(l+r)*0.5; if(cal(mid)>=E) l=mid; else r=mid; } double ans=0; for(int i=1;i<=n;i++) ans+=s[i]/x[i]; printf("%.10lf",ans); return 0; }