BZOJ 几道水题 2014-4-22
BZOJ P1088 [SCOI2005]扫雷Mine
可以发现确定前两格的个数后,后面的格子都会被自动确定,所以枚举前两格个数再判定一下即可
#include <cstdio> #include <iostream> #include <cstring> using namespace std; const int N=10500; int n,m,k,ans; int a[N],b[N]; bool check() { for (int i=2;i<=n;i++) b[i+1]=a[i]-b[i]-b[i-1]; if (b[n+1]) return 0; return 1; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); if (a[1]>2) {printf("0\n"); return 0;} for (int i=0;i<=a[1];i++) { memset(b,0,sizeof(b)); b[1]=i; b[2]=a[1]-i; if (check()) ans++;} printf("%d",ans); }
BZOJ P2561 最小生成树
给你一个图,问你最小删掉几条边使边(u,v)能出现在最大生成树和最小生成树上
回忆Kruskal算法,某一条边能放入最小生成树只有比这条边小的边不能把(u,v)连接到一个连通块内
跑一边u到v的最小割即可
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=20100,M=1001000,inf=~0U>>1; int fl[M],no[1001000],next[M],op[M],tot=0; int a[M],b[M],c[M],num[N],d[N],base[N],S,T,t,n,m; int add(int x,int y) { no[++tot]=y; fl[tot]=1; next[tot]=base[x]; op[tot]=tot+1; base[x]=tot; no[++tot]=x; fl[tot]=0; next[tot]=base[y]; op[tot]=tot-1; base[y]=tot; } int sap(int u,int flow) { if (u==T) return flow; int tmp,now=0; for (int i=base[u];i;i=next[i]) { int x=no[i]; if (fl[i]<=0||d[u]!=d[x]+1) continue; tmp=sap(x,min(flow-now,fl[i])); now+=tmp; fl[i]-=tmp; fl[op[i]]+=tmp; if (now==flow) return now; } if (d[S]>=n) return now; num[d[u]]--; if (!num[d[u]]) d[S]=n; num[++d[u]]++; return now; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]); scanf("%d%d%d",&S,&T,&t); for (int i=1;i<=m;i++) if (c[i]<t) add(a[i],b[i]),add(b[i],a[i]); num[0]=n; int ans=0; while (d[S]<n) ans+=sap(S,inf); memset(base,0,sizeof(base)); tot=0; for (int i=1;i<=m;i++) if (c[i]>t) add(a[i],b[i]),add(b[i],a[i]); memset(num,0,sizeof(num)); num[0]=n; memset(d,0,sizeof(d)); while (d[S]<n) ans+=sap(S,inf); printf("%d\n",ans); }
BZOJ P1296 [SCOI2009]粉刷匠
很显然对每一行单独处理后,得到这一行分成K段的最大收益后,这就是一个分组背包
对每一行,状态表示为Dp[i][k][w],表示已分成K段,目前染成颜色W的最大收益
DP[i][k][w]=max(Dp[i-1][k][w],Dp[i-1][k-1][!w])+(w==col[i])
#include <cstdio> #include <iostream> #include <cstring> #include <string> #include <algorithm> using namespace std; int dp[61][61][4],tmp[2501],f[2510]; int ans=0,n,m,t,c[90]; string s; int predo() { //dp[1][1][c[1]]=1; for (int i=1;i<=m;i++) for (int j=1;j<=m;j++) { int w=c[i]; dp[i][j][w]=max(dp[i-1][j-1][!w],dp[i-1][j][w])+1; dp[i][j][!w]=max(dp[i-1][j][!w],dp[i-1][j-1][w]); tmp[j]=max(tmp[j],dp[i][j][w]); tmp[j]=max(tmp[j],dp[i][j][!w]); } } int dpit() { for (int i=t;i;i--) for (int j=1;j<=m;j++) if (tmp[j]>0&&i>=j) f[i]=max(f[i],f[i-j]+tmp[j]),ans=max(ans,f[i]); } int main() { //freopen("1.in","r",stdin); scanf("%d%d%d",&n,&m,&t); for (int i=1;i<=n;i++) { cin>>s; memset(dp,0,sizeof(dp)); memset(tmp,0,sizeof(tmp)); for (int j=0;j<m;j++) if (s[j]=='0') c[j+1]=0;else c[j+1]=1; predo(); dpit(); } printf("%d\n",ans); return 0; }
BZOJ P1787 [Ahoi2008]Meet 紧急集合
树上到两点距离和最短的点一定在两点之间的路径上,三个点两两之间路径必然有一个交点(不然就是个环了)
用倍增就求出三个LCA再判定一下即可。
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int N=500010,k=30,inf=~0U>>1; int f[N],dp[N][k+1],d[N]; bool vis[N]; int n,m,x,y,z,maxx,ans,tot; int base[N],next[N*2],now[N*2]; int add(int x,int y) { now[++tot]=y; next[tot]=base[x]; base[x]=tot; } int dfs(int u) { vis[u]=1; for (int i=base[u];i;i=next[i]) if (!vis[now[i]]) { int x=now[i]; dp[x][0]=u; d[x]=d[u]+1; dfs(x); } } int lca(int x,int y) { if (x==y) return x; if (d[x]<d[y]) swap(x,y); for (int i=k;i>=0;i--) if (d[dp[x][i]]>=d[y]) x=dp[x][i]; if (x==y) return x; for (int i=k;i>=0;i--) if (dp[x][i]!=dp[y][i]) x=dp[x][i],y=dp[y][i]; return dp[x][0]; } int abs(int x){if (x<0) return -x; return x; } int dist(int x,int y) { int u=lca(x,y); return d[x]-d[u]+d[y]-d[u];} int main() { //freopen("1.in","r",stdin); scanf("%d%d",&n,&m); for (int i=1;i<=n-1;i++) {scanf("%d%d",&x,&y); add(x,y); add(y,x);} memset(vis,0,sizeof(vis)); d[0]=-1; dfs(1); for (int i=1;i<=k;i++) for (int j=1;j<=n;j++) dp[j][i]=dp[dp[j][i-1]][i-1]; while (m--) { int a,b,c,ans1=inf,ans0,l,x; scanf("%d%d%d",&a,&b,&c); l=lca(a,b); x=d[a]-d[l]+d[b]-d[l]+dist(l,c); if (x<ans1||(x==ans1&&l<ans0)) { ans1=x,ans0=l; } l=lca(b,c); x=d[b]-d[l]+d[c]-d[l]+dist(l,a); if (x<ans1||(x==ans1&&l<ans0)) { ans1=x,ans0=l; } l=lca(a,c); x=d[a]-d[l]+d[c]-d[l]+dist(l,b); if (x<ans1||(x==ans1&&l<ans0)) { ans1=x,ans0=l; } printf("%d %d\n",ans0,ans1); } }
BZOJ P2516 [NOI2003]Editor
为Rope打造的题
#include <cstdio> #include <ext/rope> #include <iostream> #include <cstring> #include <string> using namespace std; using namespace __gnu_cxx; int n,now,k; char s[2500005],s1[50]; crope R; int main() { //freopen("1.in","r",stdin); scanf("%d",&n); while (n--) { scanf("%s",s1); if (s1[0]=='M') scanf("%d",&now); if (s1[0]=='P') now--; if (s1[0]=='N') now++; if (s1[0]=='I') {scanf("%d%*c",&k); for (int i=0;i<k;i++) do { scanf("%c",&s[i]); }while(s[i]=='\n'); s[k]=0; R.insert(now,s);} if (s1[0]=='D') scanf("%d",&k),R.erase(now,k); if (s1[0]=='G') scanf("%d",&k),R.copy(now,k,s),s[k]=0,puts(s); } return 0; }
BZOJ P1293 [SCOI2009]生日礼物
对于每个确定的右端点,它左端点的最大值就是所有颜色最后一次出现位置的最小值
O(N*K)可以水过
#include <cstdio> #include <iostream> #include <algorithm> using namespace std; const int N=2001000; long long M=2147483646; typedef struct seg{int co,x;}seg; int n,m,k,tot=0; seg a[N]; int pre[70]; bool cmp(seg x,seg y) {return x.x<y.x;} int scan(int &x){ char c; while(c=getchar(),c<'0'||c>'9');x=c-'0'; while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0'; } int main() { scanf("%d",&n); scanf("%d",&m); for (int i=1;i<=m;i++) pre[i]=0; for (int i=1;i<=m;i++) { scanf("%d",&k); while (k--) scanf("%d",&a[++tot].x),a[tot].co=i; } sort(a+1,a+tot+1,cmp); long long ans=M; for (int i=1;i<=n;i++) { int minn=M; pre[a[i].co]=a[i].x; for (int j=1;j<=m;j++) minn=min(pre[j],minn); if (minn==0) continue; if (a[i].x-minn<ans) ans=a[i].x-minn; } printf("%d\n",ans); return 0; }
BZOJ P1051 [HAOI2006]受欢迎的牛
tarjan算法即可,缩点后找出度为0并唯一的强连通分量的大小输出
#include <cstdio> #include <iostream> #include <stack> #include <algorithm> #include <vector> using namespace std; stack<int>S; const int maxn=50101; int n,m,tot,a[maxn],b[maxn],dfn[maxn]; int out[maxn],size[maxn],low[maxn],f[maxn]; bool vis[maxn]; int num=0; vector<int>G[maxn]; int tarjan(int u) { dfn[u]=low[u]=++tot; S.push(u); vis[u]=1; for (int i=0;i<G[u].size();i++) { int x=G[u][i]; if (vis[x]) low[u]=min(low[u],dfn[x]); else tarjan(x),low[u]=min(low[u],low[x]); } if (dfn[u]!=low[u]) return 0; num++; while (S.top()!=u) f[S.top()]=num,size[num]++,S.pop(); S.pop(); f[u]=num; size[num]++; } int main() { //freopen("1.in","r",stdin); scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) {scanf("%d%d",&a[i],&b[i]); G[a[i]].push_back(b[i]); } for (int i=1;i<=n;i++) if (!vis[i]) tarjan(i); for (int i=1;i<=m;i++) if (f[a[i]]!=f[b[i]]) out[f[a[i]]]++; int cnt=0,k=0; for (int i=1;i<=num;i++) if (!out[i]) cnt+=size[i],k++; if (k==1) printf("%d\n",cnt); else printf("0\n"); }
BZOJ P1015 [JSOI2008]星球大战starwar
离线并查集倒序加边即可
#include <cstdio> #include <iostream> #include <cstdio> #include <vector> const int maxn=400010; using namespace std; bool bo[maxn]; int lis[maxn],f[maxn],ans[maxn]; vector<int>G[maxn]; int sf(int x) { if (f[x]==x) return x; return f[x]=sf(f[x]); } int bfs(int x) { int ans=0; for (int i=0;i<G[x].size();i++) { int y=G[x][i]; if (bo[y]) continue; y=sf(y); int fx=sf(x); if (y!=fx) f[y]=fx,ans++; } return ans; } int main() { //freopen("1.in","r",stdin); int n,m,k; int x,y; scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) {scanf("%d%d",&x,&y),x++,y++; G[x].push_back(y),G[y].push_back(x);} scanf("%d",&k); for (int i=1;i<=n;i++) f[i]=i; for (int i=1;i<=k;i++) scanf("%d",&lis[i]),bo[++lis[i]]=1; for (int i=1;i<=n;i++) if (!bo[i]) bfs(i); for (int i=1;i<=n;i++) if (sf(i)==i) ans[k]++; ans[k]-=k; for (int i=k;i;i--) { int x=lis[i]; bo[x]=0; ans[i-1]=ans[i]-bfs(x)+1; } for (int i=0;i<=k;i++) printf("%d\n",ans[i]); }
BZOJ P2875 [Noi2012]随机数生成器
矩阵乘法模板题
#include <cstdio> #include <iostream> using namespace std; typedef long long ll; typedef struct matrix{ll a[4][4];}matrix; ll x0,m,c,a,n,g; matrix ma,mb,tmp; ll multi(ll x,ll y,ll m) { ll tmp=x; ll ans=0; while (y) { if (y&1) ans=(ans+tmp)%m; tmp=(tmp+tmp)%m; y/=2;} return ans%m; } matrix mult(matrix ma,matrix mb,ll n,ll r,ll c,ll m) { matrix mc=tmp; for (int i=1;i<=r;i++) for (int j=1;j<=c;j++) for (int k=1;k<=n;k++) { mc.a[i][j]=(mc.a[i][j]+multi(ma.a[i][k],mb.a[k][j],m))%m;} return mc; } matrix power(matrix ma,ll r,ll c,ll m,ll b) { if (b==1) return ma; matrix mc=power(ma,r,c,m,b/2); mc=mult(mc,mc,r,c,r,m); if (b&1) mc=mult(mc,ma,r,c,r,m); return mc; } int main() { //freopen("1.in","r",stdin); cin>>m>>a>>c>>x0>>n>>g; ma.a[1][1]=a; ma.a[1][2]=1; ma.a[2][1]=0; ma.a[2][2]=1; mb.a[1][1]=x0; mb.a[2][1]=c; ma=power(ma,2,2,m,n); mb=mult(ma,mb,2,2,1,m); cout<<mb.a[1][1]%g; }
BZOJ P2456 mode
看起来很水但要求不能用数组,采用抵消掉思想
每进一个数,如果之前有没被抵消掉数就抵消一次
如果出项一半以上,它一定不能被抵消完,剩下那个数就是要找到数
#include <cstdio> using namespace std; int n,tot,now,tmp; int main() { scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%d",&tmp); if (tot==0) now=tmp; if (now==tmp) tot++; else tot--; } printf("%d\n",now);}
BZOJ P2190 [SDOI2008]仪仗队
一个点到原点的直线上有整点,那么这两个数的gcd就不是1
算一下(1~N-1)中的欧拉函数和就可以了
#include <cstdio> #include <iostream> using namespace std; const int maxn=40010; long long phi[maxn]; bool b[maxn]; int n; int main() { scanf("%d",&n); long long ans=0; for (int i=1;i<=n;i++) phi[i]=i; for (int i=2;i<=n;i++) if (!b[i]) { phi[i]--; for (int j=2;j*i<=n;j++) phi[i*j]=(phi[i*j]/i)*(i-1),b[i*j]=1; } for (int i=1;i<=n-1;i++) ans+=phi[i]*2; //if (n<2) printf("0\n"); printf("%lld\n",ans+1); }
BZOJ P2818 Gcd
欧拉函数和.....
#include <cstdio> #include <cstring> using namespace std; typedef long long ll; const int maxn=12000000; ll phi[maxn]; bool b[maxn]; int prim[maxn]; int main() { int n; int tot=0; scanf("%d",&n); for (int i=2;i<=n;i++) phi[i]=i; for (int i=2;i<=n;i++) if (!b[i]) { prim[++tot]=i; phi[i]=i-1; for (int j=2;j*i<=n;j++) b[i*j]=1,phi[i*j]=(phi[i*j]/i)*(i-1); } for (int i=2;i<=n;i++) phi[i]=phi[i]+phi[i-1]; ll ans=0; for (int i=1;i<=tot;i++) ans+=2*phi[n/prim[i]]+1; printf("%lld\n",ans); }
BZOJ P1202 [HNOI2005]狡猾的商人
差分约束判定即可
#include <cstdio> #include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn=3010; typedef struct seg{int x,next,f;}seg; seg v[maxn]; int base[maxn],cnt[maxn],d[maxn]; int n,m,t,x,y,z,tot=0; bool vis[maxn]; int add(int x,int y,int z) { v[++tot].x=y; v[tot].f=z; v[tot].next=base[x]; base[x]=tot; } queue<int>Q; bool spfa() { while (!Q.empty()) Q.pop(); for (int i=0;i<=n;i++) Q.push(i),vis[i]=1,d[i]=cnt[i]=0; while (!Q.empty()) { int u=Q.front(); Q.pop(); vis[u]=0; for (int i=base[u];i;i=v[i].next) { int x=v[i].x; if (d[x]<=d[u]+v[i].f) continue; if (++cnt[x]>n) return 0; d[x]=d[u]+v[i].f; if (!vis[x]) vis[x]=1,Q.push(x); } } return 1; } int main() { //freopen("1.in","r",stdin); scanf("%d",&t); while (t--) { scanf("%d%d",&n,&m); tot=0; memset(base,0,sizeof(base)); while (m--) { scanf("%d%d%d",&x,&y,&z); add(x-1,y,z); add(y,x-1,-z); } if (spfa())puts("true");else puts("false"); } }
BZOJ P1491 [NOI2007]社交网络
floyed用乘法原理计算最短路的数量
再一遍floyed查看中转点是否能在最短路径上
#include <cstdio> #include <cstring> #include <vector> using namespace std; const int maxn=2560; int n,m,r,c; int g[101][101]; int pos[maxn]; bool vis[maxn]; vector<int>G[maxn*500]; int dx[8],dy[8]; bool dfs(int x) { for (int i=0;i<G[x].size();i++) { int y=G[x][i]; if (vis[y]) continue;vis[y]=1; if (!pos[y]||dfs(pos[y])) {pos[y]=x; return 1;} } return 0; } int main() { //freopen("1.in","r",stdin); scanf("%d%d%d%d",&n,&m,&r,&c); int tot=0; char s[maxn]; for (int i=1;i<=n;i++) { scanf("%s",&s); for (int j=1;j<=m;j++) if (s[j-1]=='.') tot++,g[i][j]=tot; } dx[0]=r; dx[1]=r; dx[2]=c; dx[3]=c; dy[0]=c; dy[1]=-c; dy[2]=-r; dy[3]=r; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (g[i][j]) for (int k=0;k<=3;k++) { int x=i+dx[k],y=j+dy[k]; if (x>0&&x<=n&&y>0&&y<=m) if (g[x][y]) G[g[i][j]].push_back(g[x][y]); } int ans=0; //printf("%d",tot); for (int i=1;i<=tot;i++) {memset(vis,0,sizeof(vis));if (dfs(i)) ans++;} printf("%d\n",tot-ans); }