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
"); 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);  
    }
View Code

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
",ans);   
     }
View Code

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
",ans);
     return 0;  
    } 
View Code

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
",ans0,ans1);
    }
   }
View Code

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]=='
');
        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;
  }
View Code

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
",ans);
     return 0;
   }
View Code

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
",cnt); else printf("0
"); 
     }
View Code

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
",ans[i]);
}
View Code

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;
  }
View Code

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
",now);}
View Code

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
"); 
     printf("%lld
",ans+1);
   }
View Code

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
",ans);
     }
View Code

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");
       }
     }
View Code

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
",tot-ans);
  
  }
View Code
原文地址:https://www.cnblogs.com/williamchenwl/p/3681706.html