标签:tin == 一个 裸题 code pop eof str amp
太菜了,于是打算做做NOIP的题。
第一题:jzoj5305 C
一个仙人掌的路径方案计数。可以考虑到一个仙人掌路径出现多种情况的唯一可能是经过了一个环,而一个环的走法无非是走上或者走下。
因此随意Tarjan缩个点,然后仙人掌就被缩成了树,在这个树上跑一下倍增即可。
#include<bits/stdc++.h> const int N=2e5+5; const int M=6*1e5+5; const int yql=1e9+7; using namespace std; int low[N],dfn[N],cnt=0,head[N],tot=0; struct Edge{int u,v,next;}G[M],T[M]; stack<int> st; int n,m; int fa[N],dep[N],anc[18][N],bin[20],scnt=0,scc[N],vis[N],c[N],lim=0; inline void addedge(int u,int v){ G[++tot].u=u;G[tot].v=v;G[tot].next=head[u];head[u]=tot; G[++tot].u=v;G[tot].v=u;G[tot].next=head[v];head[v]=tot; } inline int read(){ int f=1,x=0;char ch; do{ch=getchar();if(ch==‘-‘)f=-1;}while(ch<‘0‘||ch>‘9‘); do{x=x*10+ch-‘0‘;ch=getchar();}while(ch>=‘0‘&&ch<=‘9‘); return f*x; } inline void Tarjan(int u,int f){ dfn[u]=low[u]=++cnt;st.push(u); vis[u]=1; for(int i=head[u];i;i=G[i].next){ int v=G[i].v;if(v==f)continue; if(!dfn[v]){Tarjan(v,u);low[u]=min(low[u],low[v]);} else if(vis[v])low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]){ int cur=st.top();c[u]=cur!=u; while(st.top()!=u)scc[st.top()]=u,vis[st.top()]=0,st.pop(); scc[st.top()]=u;vis[st.top()]=0;st.pop(); } } inline void dfs(int u,int f){ dfn[u]=1;c[u]+=c[f]; dep[u]=dep[f]+1; anc[0][u]=f; for(int i=head[u];i;i=G[i].next)if(G[i].v!=f&&!dfn[G[i].v])dfs(G[i].v,u); } inline int calc(int u,int v){ if(dep[u]<dep[v])swap(u,v); int x=u,y=v; for(int i=lim;i>=0;i--)if(dep[anc[i][u]]>=dep[v])u=anc[i][u]; if(u==v)return bin[c[x]-c[anc[0][y]]]; for(int i=lim;i>=0;i--)if(anc[i][u]!=anc[i][v])u=anc[i][u],v=anc[i][v]; return bin[c[x]+c[y]-c[anc[0][u]]-c[anc[1][u]]]; } int main(){ bin[0]=1; n=read();m=read(); for(int i=1;i<=20;i++)bin[i]=(bin[i-1]*2)%yql; for(int i=1;i<=m;i++){ T[i].u=read();T[i].v=read(); addedge(T[i].u,T[i].v); addedge(T[i].v,T[i].u); } for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(i,0); tot=0; memset(head,0,sizeof(head));for(int i=1;i<=n;i++)dfn[i]=0; for(int i=1;i<=m;i++)if(scc[T[i].u]!=scc[T[i].v]){ addedge(scc[T[i].u],scc[T[i].v]);addedge(scc[T[i].v],scc[T[i].u]); } for(int i=1;i<=n;i++)if(!dfn[i])dfs(i,0); lim=(int)(log(n)/log(2)); for(int j=1;j<=lim;j++)for(int i=1;i<=n;i++)anc[j][i]=anc[j-1][anc[j-1][i]]; int q=read(); while(q--){ int u=read(),v=read(); printf("%d\n",calc(scc[u],scc[v])); } }
第二题:jzoj5306 棋盘游戏
考虑到行列是独立的,这就是一个威佐夫博弈的裸题。
所以根据威佐夫博弈的公式算出delta*.618,如果=x先手必输,反之必胜。
#include<bits/stdc++.h> using namespace std; int x,y; inline int read(){ int f=1,x=0;char ch; do{ch=getchar();if(ch==‘-‘)f=-1;}while(ch<‘0‘||ch>‘9‘); do{x=x*10+ch-‘0‘;ch=getchar();}while(ch>=‘0‘&&ch<=‘9‘); return f*x; } inline int calc(int x,int y){ if(x>y)swap(x,y); int ans=floor((1.0+sqrt(5.0))/2*(y-x)); return ans; } int main(){ int T=read(); while(T--){ int x=read(),y=read(); int ans=calc(x,y); if(ans==x)puts("Alphago");else puts("Amphetamine"); } }
第三题:jzoj5307偷窃,bzoj4950(WF2017)
这就是Claris在51nod讲过的wf原题,考虑下最大值一行仅需要留一个,约束关系形成二分图的关系,跑一下最大匹配即可。
#include<bits/stdc++.h> const int N=110; const int M=10010; using namespace std; typedef long long ll; ll ans=0; int a[N][N]; int n,m; int head[M],tot=0,vis[N],f[M],m1[N],m2[N]; struct Edge{int u,v,next;}G[M<<1]; inline void addedge(int u,int v){ G[++tot].u=u;G[tot].v=v;G[tot].next=head[u];head[u]=tot; } inline int read(){ int f=1,x=0;char ch; do{ch=getchar();if(ch==‘-‘)f=-1;}while(ch<‘0‘||ch>‘9‘); do{x=x*10+ch-‘0‘;ch=getchar();}while(ch>=‘0‘&&ch<=‘9‘); return f*x; } inline int dfs(int u){ for(int i=head[u];i;i=G[i].next){ int v=G[i].v;if(vis[v])continue; vis[v]=1; if(!f[v]||dfs(f[v])){f[v]=u;return 1;} } return 0; } int main(){ n=read();m=read();ans=0; for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){ a[i][j]=read();m1[i]=max(m1[i],a[i][j]);m2[j]=max(m2[j],a[i][j]); ans+=a[i][j]; } for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(a[i][j]){ --ans;if(m1[i]>1&&m1[i]==m2[j])addedge(i,j); } for(int i=1;i<=n;i++)if(m1[i])ans-=m1[i]-1; for(int i=1;i<=m;i++)if(m2[i])ans-=m2[i]-1; for(int i=1;i<=n;i++)if(m1[i]){ memset(vis,0,sizeof(vis)); ans+=dfs(i)*(m1[i]-1); } cout<<ans<<endl; }
标签:tin == 一个 裸题 code pop eof str amp
原文地址:http://www.cnblogs.com/zcysky/p/7392153.html