zoukankan      html  css  js  c++  java
  • 【SDOI2010题集整合】BZOJ1922~1927&1941&1951&1952&1972&1974&1975

    BZOJ1922大陆争霸

    思路:带限制的单源最短路

    限制每个点的条件有二,路程和最早能进入的时间,那么对两个值一起限制跑最短路,显然想要访问一个点最少满足max(dis,time)

    那么每次把相连的点以及所保护的点扔进堆中,用以更新答案,不过值得注意的是,入堆的时候进行判断

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define p pair<int,int>
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    #define maxn 3010
    #define maxm 70010
    int n,m;
    struct data{int to,next,w;}edge[maxm];
    int l[maxn][maxn],lt[maxn],ll[maxn];
    int head[maxn],cnt;
    int S,T;
    
    void add(int u,int v,int w)
    {
        cnt++;
        edge[cnt].next=head[u];  head[u]=cnt;
        edge[cnt].to=v; edge[cnt].w=w;
    }
    
    int dis1[maxn],dis2[maxn];
    bool visit[maxn];
    priority_queue<p,vector<p>,greater<p> >q;
    void dijkstra()
    {
        memset(dis1,0x3f,sizeof(dis1));
        q.push(make_pair(0,S));  dis1[S]=0;
        while (!q.empty())
            {
                int now=q.top().second; q.pop(); 
                if(visit[now]) continue; visit[now]=1;
                //printf("%d
    ",now);
                int dis=max(dis1[now],dis2[now]);
                for (int i=head[now]; i; i=edge[i].next)    
                    if (dis+edge[i].w<dis1[edge[i].to])
                        {
                            dis1[edge[i].to]=dis+edge[i].w;
                            int tmp=max(dis1[edge[i].to],dis2[edge[i].to]);
                            if(!ll[edge[i].to]) q.push(make_pair(tmp,edge[i].to));
                        }
                //printf("%d
    ",lt[now]);
                for (int i=1; i<=lt[now]; i++)
                    {
                        int noww=l[now][i]; ll[noww]--;
                        dis2[noww]=max(dis2[noww],dis);
                        if (!ll[noww]) q.push(make_pair(max(dis1[noww],dis2[noww]),noww));
                    }
            }
    }
    
    int main()
    {
        n=read(),m=read();
        for (int i=1; i<=m; i++)
            {
                int u=read(),v=read(),w=read();
                if (u!=v) add(u,v,w);
            }
        for (int i=1; i<=n; i++)
            {
                ll[i]=read();
                for (int j=1; j<=ll[i]; j++)
                    {
                        int x=read();
                        l[x][++lt[x]]=i;
                    }
            }
        S=1;T=n;
        dijkstra();
    //  for (int i=1; i<=n; i++)
    //      printf("%d %d
    ",dis1[i],dis2[i]);
        printf("%d
    ",max(dis1[T],dis2[T]));
        return 0;
    }
    大陆争霸

    BZOJ1923外星千足虫

    思路:高斯消元解xor方程组

    对于给出的信息,显然是一个方程组,对于方程组,考虑高斯消元求解,不过这里的方程组带取模,所以考虑位运算xor,套高斯消元即可

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<bitset>
    #include<cstring>
    #include<cstdlib>
    using namespace std;
    bitset <1005> A[2010];
    int B[2010],n,m,ans;
    char s[2010];
    int Gauss()
    {
        for (int i=1; i<=n; i++)
            {
                int j=i;
                while (j<=m && !A[j][i]) j++;
                if (j==m+1) return 0;
                ans=max(ans,j);
                swap(A[i],A[j]);
                for (int k=1; k<=m; k++)
                    if (i!=k && A[k][i])
                        A[k]^=A[i];
            }
        return 1;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1; i<=m; i++)
            {
                scanf("%s%d",s,&B[i]);
                for (int j=0; j<=n-1; j++) A[i][j+1]=s[j]-'0';
                A[i][n+1]=B[i];
            }
        int OK=Gauss();
        if (!OK) {puts("Cannot Determine"); return 0;}
        printf("%d
    ",ans);
        for (int i=1; i<=n; i++)
            if (A[i][n+1]) puts("?y7M#");
                else puts("Earth");
        return 0;
    }
    外形千足虫

    BZOJ1924所驼门王的宝藏

    思路:Tarjan+拓扑图DP

    首先将给出的各种关系相连,这里比较麻烦的是,需要用vector去记录行和列的情况,map判断八连通的情况

    选择一个 横/竖 格向同 行/列 所有点连单向边,对 横/竖 格连双向边;八连通直接连;Tarjan并重构,做一个 傻傻的DP即可

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<map>
    #include<algorithm>
    using namespace std;
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    #define maxn 100010
    int dx[8]={0,0,1,1,1,-1,-1,-1},dy[8]={1,-1,0,1,-1,0,1,-1};
    struct EdgeNode{int next,to;}edge[maxn*10],road[maxn*10];
    int cnt,tot,head[maxn],last[maxn];
    void addedge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
    void insertedge(int u,int v) {if (u==v) return; addedge(u,v);}
    void addroad(int u,int v) {tot++; road[tot].next=last[u]; last[u]=tot; road[tot].to=v;}
    void insertroad(int u,int v) {addroad(u,v);}
    int x[maxn],y[maxn],t[maxn],dfn[maxn],low[maxn],stack[maxn],num[maxn],belong[maxn],dp[maxn];
    bool visit[maxn];
    int n,r,c,ans,top,qcnt;
    vector<int>h[maxn*10],l[maxn*10];
    map<int,int>mp[maxn*10];
    void BuildGraph()
    {
        for (int i=1; i<=r; i++) 
            {    
                int hn=h[i].size(),now=0;
                for (int j=0; j<=hn-1; j++) if (t[h[i][j]]==1) {now=h[i][j]; break;}
                for (int j=0; j<=hn-1; j++) {insertedge(now,h[i][j]); if (t[h[i][j]]==1) insertedge(h[i][j],now);}
            }
        for (int i=1; i<=c; i++)
            {
                int ln=l[i].size(),now=0;
                for (int j=0; j<=ln-1; j++) if (t[l[i][j]]==2) {now=l[i][j]; break;}
                for (int j=0; j<=ln-1; j++) {insertedge(now,l[i][j]); if (t[l[i][j]]==2) insertedge(l[i][j],now);}
            }
        for (int i=1; i<=n; i++)
            if (t[i]==3)
                for (int xx,yy,j=0; j<=7; j++)
                    {
                        xx=x[i]+dx[j],yy=y[i]+dy[j];
                        if (mp[xx][yy]) insertedge(i,mp[xx][yy]);
                    }
    }
    void Tarjan(int x) 
    { 
        dfn[x]=low[x]=++tot; visit[x]=1; stack[++top]=x; 
        for (int i=head[x]; i; i=edge[i].next) 
            { 
                if (!dfn[edge[i].to]) 
                    { 
                        Tarjan(edge[i].to); 
                        if (low[edge[i].to]<low[x]) low[x]=low[edge[i].to]; 
                    } 
                else 
                    if(visit[edge[i].to] && dfn[edge[i].to]<low[x]) 
                        low[x]=dfn[edge[i].to]; 
            }
        if (dfn[x]==low[x]) 
            { 
                int uu=0; qcnt++; 
                while (x!=uu) 
                    uu=stack[top--],num[qcnt]++,visit[uu]=0,belong[uu]=qcnt;  
            } 
    } 
    void reBuildGraph()
    {
        for (int i=1; i<=n; i++) 
            for (int j=head[i]; j; j=edge[j].next) 
                if (belong[i]!=belong[edge[j].to]) 
                    insertroad(belong[i],belong[edge[j].to]); 
    }
    void DP(int now)
    {
        visit[now]=1;
        for (int i=last[now]; i; i=road[i].next)
            {
                if (!visit[road[i].to]) DP(road[i].to);
                dp[now]=max(dp[now],dp[road[i].to]);
            }
        dp[now]+=num[now];
        ans=max(ans,dp[now]);
    }
    int main()
    {
        n=read(); r=read(); c=read();
        for (int i=1; i<=n; i++)
            {
                x[i]=read(),y[i]=read(),t[i]=read();
                mp[x[i]][y[i]]=i; h[x[i]].push_back(i); l[y[i]].push_back(i);
            }
        BuildGraph();
        for (int i=1; i<=n; i++) if (!dfn[i]) Tarjan(i);
        reBuildGraph();
        for (int i=1; i<=qcnt; i++) if (!visit[i]) DP(i);
        printf("%d
    ",ans); 
        return 0;
    }
    所驼门王的宝藏

    BZOJ1925地精部落

    思路:DP 抖动子序列

    f[i][j]表示以j为开头的长度为i的抖动子序列个数,这种序列有些性质:

    1.在一个1-n的排列构成的抖动序列里,交换任意的元素i和i+1,它仍然是符合条件的抖动序列 
    2.一个开头上升的抖动序列翻转过来就变成了符合条件的开头下降的 

    那么就可以转移了$f[i][j]=f[i-1][j]+f[i-1][i-j]$,最后的结果当然就是$f[n][n]*2$ 还有就是这题需要滚动数组

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    #define maxn 5010
    int n,p;
    int f[2][maxn];
    int main()
    {
        n=read(),p=read();
        f[1][1]=1;
        for (int i=2; i<=n; i++)
            for (int j=1; j<=i; j++)
                f[i&1][j]=(f[i&1][j-1]%p+f[(i&1)^1][i-j]%p)%p;
        printf("%d
    ",(f[n&1][n]*2)%p);
        return 0;
    }
    地精部落

    BZOJ1926粟粟的书架

    思路:前缀和  主席树  二合一  (二维莫队)

    用二合一的方法水过这道题,对于行数=1的情况,很显然裸主席树;对于矩阵的情况,范围允许$n^{2}$的,考虑预处理两个二维的前缀和

    sum[i][j][k]表示>=k的数的和,num[i][j][k]表示>=k的数的个数 那么询问的时候二分一下就好

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-')f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f; 
    }
    int R,C,M;
    #define maxn 500010
    int Sum[maxn*20],Num[maxn*20],ll[maxn*20],rr[maxn*20],root[maxn<<2],sz;
    int sum[210][210][1010],num[210][210][1010],p[210][210];
    void Insert(int l,int r,int &now,int fat,int val)
    {
        now=++sz; Sum[now]=Sum[fat]+val; Num[now]=Num[fat]+1;
        if (l==r) return;
        ll[now]=ll[fat],rr[now]=rr[fat];
        int mid=(l+r)>>1;
        if (val<=mid) Insert(l,mid,ll[now],ll[fat],val);
        else Insert(mid+1,r,rr[now],rr[fat],val);
    }
    int Query(int l,int r,int L,int R,int kth)
    {
        if (Sum[root[R]]-Sum[root[L-1]]<kth) return -1;
        L=root[L-1]; R=root[R];
        int re=0;
        while (l<r)
            {
                int mid=(l+r)>>1,tmp=Sum[rr[R]]-Sum[rr[L]];
                if (tmp<kth) {re+=Num[rr[R]]-Num[rr[L]]; kth-=tmp; r=mid; L=ll[L]; R=ll[R];}
                    else {l=mid+1; L=rr[L]; R=rr[R];}
            }
        re+=(kth+l-1)/l;
        return re;
    }
    void Part1()
    {
        for (int i=1; i<=C; i++) Insert(1,1000,root[i],root[i-1],read());
        while (M--)
            {
                int x1=read(),y1=read(),x2=read(),y2=read(),h=read();
                int ans=Query(1,1000,y1,y2,h);
                if (ans==-1) {puts("Poor QLW"); continue;}
                printf("%d
    ",ans);
            }
    }
    int GetSum(int x1,int y1,int x2,int y2,int k) {return sum[x1-1][y1-1][k]+sum[x2][y2][k]-sum[x1-1][y2][k]-sum[x2][y1-1][k];}
    int GetNum(int x1,int y1,int x2,int y2,int k) {return num[x1-1][y1-1][k]+num[x2][y2][k]-num[x1-1][y2][k]-num[x2][y1-1][k];}
    void Part2()
    {
        int maxx=0;
        for (int i=1; i<=R; i++)
            for (int j=1; j<=C; j++)
                p[i][j]=read(),maxx=max(maxx,p[i][j]);
        for (int i=1; i<=R; i++)
            for (int j=1; j<=C; j++)
                for (int k=1; k<=maxx; k++)
                    num[i][j][k]=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+(p[i][j]>=k?1:0),
                    sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k]+(p[i][j]>=k?p[i][j]:0);
        while (M--)
            {
                int x1=read(),y1=read(),x2=read(),y2=read(),h=read();
                int l=0,r=maxx+1,k=-1;
                while (l+1<r)
                    {
                        int mid=(l+r)>>1;
                        if (GetSum(x1,y1,x2,y2,mid)>=h) l=mid,k=mid; else r=mid;
                    }
                if (k==-1) {puts("Poor QLW"); continue;}
                printf("%d
    ",GetNum(x1,y1,x2,y2,k)-(GetSum(x1,y1,x2,y2,k)-h)/k);
            }
    }
    int main()
    {
        R=read(),C=read(),M=read();
        if (R==1) Part1(); else Part2();
        return 0;
    }
    粟粟的书架

    BZOJ1927星际竞速

    思路:最小费用流 

    每个星球拆成两个点,入点$u_{i}$出点$v_{i}$

    $S-->u_{i}$  容量为1,费用为0;

    $S-->v_{i}$  容量为1,费用为0;

    $v_{i}-->T$  容量为1,费用为0;

    读入U,V,C如果$V>U$则交换,$U_{u_{i}}-->V_{v_{i}}$  容量为1,费用为C;

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define inf 0x7fffffff
    struct data{
        int next,to,v,c;
    }edge[2000010];
    int cnt=1,head[2010];
    int q[20010],h,t;
    bool mark[2010];
    bool visit[2010];
    int n,m;
    int ans,num;
    int dis[20010];
    
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    
    void add(int u,int v,int cap,int cost)
    {
        cnt++;edge[cnt].to=v;
        edge[cnt].next=head[u];head[u]=cnt;
        edge[cnt].v=cap;edge[cnt].c=cost;   
    }
    
    void insert(int u,int v,int cap,int cost)
    {
        add(u,v,cap,cost);add(v,u,0,-cost);
    }
    
    bool spfa()
    {
        memset(visit,0,sizeof(visit));
        for (int i=0; i<=num; i++) dis[i]=inf;
        h=0,t=1;
        q[0]=num;visit[num]=1;dis[num]=0;
        while (h<t)
            {
                int now=q[h];h++;visit[now]=0;
                for (int i=head[now]; i; i=edge[i].next)
                    if (edge[i^1].v && dis[now]-edge[i].c<dis[edge[i].to])
                        {   
                            dis[edge[i].to]=dis[now]-edge[i].c;
                            if (!visit[edge[i].to])
                                {
                                    visit[edge[i].to]=1;
                                    q[t++]=edge[i].to;
                                }
                        }
            }
        return dis[0]!=inf;
    }
    
    int dfs(int loc,int low)
    {
        mark[loc]=1;
        if (loc==num)   return low;
        int w,used=0;
        for (int i=head[loc]; i; i=edge[i].next)
            if (dis[edge[i].to]==dis[loc]-edge[i].c && edge[i].v && !mark[edge[i].to])
                {
                    w=dfs(edge[i].to,min(low-used,edge[i].v));
                    ans+=w*edge[i].c;
                    edge[i].v-=w;edge[i^1].v+=w;
                    used+=w;if (used==low)  return low;
                }
        return used;
    }
    
    void zkw()
    {
        int tmp=0;
        while (spfa())
            {
                mark[num]=1;
                while (mark[num])
                    {
                        memset(mark,0,sizeof(mark));
                        tmp+=dfs(0,inf);
                    }
            }
    }
    
    int main()
    {
        n=read();m=read();num=2*n+1;
        for (int i=1; i<=n; i++)
            {
                int cost=read();
                insert(0,i,1,0);
                insert(i+n,num,1,0);
                insert(0,i+n,1,cost);
            }
        for (int i=1; i<=m; i++)
            {
                int x=read(),y=read(),z=read();
                if (x>y) {int temp=x;x=y;y=temp;}
                insert(x,y+n,1,z);
            }
        zkw();
        printf("%d
    ",ans);
        return 0;
    }
    星际竞速

    BZOJ1941Hide and Seek

    思路: KD-Tree

    把所有点加入KD-Tree,然后枚举每个点去找他的最远、最近点,并更新答案

    需要注意的是,计算最远和最近的两个分开写,而且计算最近点的时候不能计算到自己

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm> 
    #include<cmath>
    using namespace std;
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-')f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f; 
    }
    #define inf 0x7fffffff
    #define maxn 500010
    int n,D,ans;
    struct PointNode
    {
        int l,r; int d[2],maxx[2],minn[2];
        PointNode (int x=0,int y=0) {l=r=0; d[0]=x,d[1]=y;}
        bool operator < (const PointNode & A) const {return d[D]<A.d[D];}
    }p[maxn];
    int dis(PointNode A,PointNode B) {return abs(A.d[1]-B.d[1])+abs(A.d[0]-B.d[0]);}
    struct KDTreeNode
    {
        PointNode tree[maxn<<1],Point;
        int rt,ansMax,ansMin;
        void Update(int now)
            {
                for (int i=0; i<=1; i++)
                    {
                        tree[now].minn[i]=tree[now].maxx[i]=tree[now].d[i];
                        if (tree[now].l)
                            tree[now].minn[i]=min(tree[tree[now].l].minn[i],tree[now].minn[i]),
                  tree[now].maxx[i]=max(tree[tree[now].l].maxx[i],tree[now].maxx[i]);
                        if (tree[now].r)
                            tree[now].minn[i]=min(tree[tree[now].r].minn[i],tree[now].minn[i]),
                  tree[now].maxx[i]=max(tree[tree[now].r].maxx[i],tree[now].maxx[i]);
                    }
            }    
        int BuildTree(int l,int r,int dd)
            {
                int mid=(l+r)>>1;
                D=dd; nth_element(p+l,p+mid,p+r+1);
                tree[mid]=p[mid];
                for (int i=0; i<=1; i++) tree[mid].minn[i]=tree[mid].maxx[i]=tree[mid].d[i];
                if (l<mid) tree[mid].l=BuildTree(l,mid-1,dd^1);
                if (r>mid) tree[mid].r=BuildTree(mid+1,r,dd^1);
                Update(mid);
                return mid;
            }
        int disMax(int now)
            {
                if (!now) return -inf;
                int re=0;
                for (int i=0; i<=1; i++)
                    re+=max(abs(tree[now].maxx[i]-Point.d[i]),abs(tree[now].minn[i]-Point.d[i]));
                return re;
            }
        int disMin(int now)
            {
                if (!now) return inf;
                int re=0;
                for (int i=0; i<=1; i++) re+=max(0,tree[now].minn[i]-Point.d[i]);
                for (int i=0; i<=1; i++) re+=max(0,Point.d[i]-tree[now].maxx[i]);
                return re;
            }
        void GetMax(int now)
            {
                if (!now) return;    
                int dl,dr,d0;
                d0=dis(tree[now],Point);
                ansMax=max(d0,ansMax);
                if (tree[now].l) dl=disMax(tree[now].l);
                if (tree[now].r) dr=disMax(tree[now].r);
                if (dl>dr)
                    {
                        if (dl>ansMax) GetMax(tree[now].l);
                        if (dr>ansMax) GetMax(tree[now].r);
                    }
                else
                    {
                        if (dr>ansMax) GetMax(tree[now].r);
                        if (dl>ansMax) GetMax(tree[now].l);
                    }
            }
        void GetMin(int now)
            {
                if (!now) return;
                int dl,dr,d0;
                d0=dis(tree[now],Point);
                if (d0) ansMin=min(ansMin,d0);
                if (tree[now].l) dl=disMin(tree[now].l);
                if (tree[now].r) dr=disMin(tree[now].r);
                if (dl<dr)
                    {
                        if (dl<ansMin) GetMin(tree[now].l);
                        if (dr<ansMin) GetMin(tree[now].r);
                    }
                else
                    {
                        if (dr<ansMin) GetMin(tree[now].r);
                        if (dl<ansMin) GetMin(tree[now].l);
                    }
            }
        int QueryMax(PointNode P) {Point=P; ansMax=-inf; GetMax(rt); return ansMax;}
        int QueryMin(PointNode P) {Point=P; ansMin=inf; GetMin(rt); return ansMin;}
    }KDTree;
    int main()
    {
        n=read();
        for (int x,y,i=1; i<=n; i++) x=read(),y=read(),p[i].d[0]=x,p[i].d[1]=y;
        for (int i=0; i<=1; i++) p[0].maxx[i]=-inf,p[0].minn[i]=inf;
        KDTree.rt=KDTree.BuildTree(1,n,1);
        ans=inf;
        for (int i=1; i<=n; i++)
            {
                int minn=KDTree.QueryMin(p[i]),maxx=KDTree.QueryMax(p[i]);
                ans=min(ans,maxx-minn);
            }
        printf("%d
    ",ans);
        return 0;
    }
    Hide and Seek

    BZOJ1951古代猪文

    思路:组合数取模Lucas定理、中国剩余定理

    首先弄清楚求解的东西:$G^{sum_{d|n}C_{n}^{d}} mod 999911659$

    这个东西,设指数为M,模数为P,经过费马小定理可以转化一下:$G^{M}modP=G^{Mmod(P-1)}(G!=P)$

    这里$P-1$不是质数,所以把它拆成多个质数的形式,最后用中国剩余定理合并即可

    Code:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    
    using namespace std;
    int pp[4]={2,3,4679,35617};int G,N,P=999911659;
    int jc[4][50000];
    int M[4];
    
    void exgcd(int a,int b,int &x,int &y)
    {
        if (b==0) {x=1;y=0;return;}
        exgcd(b,a%b,x,y);
        int tmp=x;x=y;y=tmp-a/b*y;
    }
    
    int quick_pow(long long a,int b,int p)
    {
        int ans=1;
        for(int i=b;i;i>>=1,a=(a*a)%p)
            if(i&1)ans=(ans*a)%p;
        return ans;
    }
    
    void cs()
    {
        jc[1][0]=jc[2][0]=jc[3][0]=jc[0][0]=1;
        for (int i=0; i<4; i++)
            for (int j=1; j<=pp[i]; j++)
                jc[i][j]=(jc[i][j-1]*j)%pp[i];
    }
    int C(int n,int m,int p)
    {
        if (n<m) return 0;
        return jc[p][n]*quick_pow(jc[p][m]*jc[p][n-m],pp[p]-2,pp[p])%pp[p];
    }
    int lucas(int n,int m,int p)
    {
        if (m==0) return 1;
        return C(n%pp[p],m%pp[p],p)*lucas(n/pp[p],m/pp[p],p)%pp[p];
    }
    
    int china()
    {
        int a1,b1,a2,b2,a,b,c,x,y;
        a1=pp[0],b1=M[0];
        for(int i=1;i<4;i++)
        {
            a2=pp[i],b2=M[i];
            a=a1;b=a2;c=b2-b1;
            exgcd(a,b,x,y);
            x=((c*x)%b+b)%b;
            b1=b1+a1*x;
            a1=a1*b;
        }
        return b1;
    }
    
    int work()
    {
        G%=P;
        for (int i=1; i*i<=N; i++)
            {
                if (N%i==0)
                    {
                        int tmp=N/i;
                        for (int j=0; j<4; j++)
                            {
                                if (tmp!=i)
                                    M[j]=(M[j]+lucas(N,i,j))%pp[j];
                                M[j]=(M[j]+lucas(N,tmp,j))%pp[j];
                            }
                    }
            }
        printf("%d
    ",quick_pow(G,china(),P));
    }
    
    int main()
    {
        cs();
        scanf("%d%d",&N,&G);
        if (G==P) {puts("0");return 0;}
        work();
        return 0;
    }
    古代猪文

    BZOJ1952城市规划

    思路: 仙人掌DP  最大点权独立集

    求解最大点权独立集,然后仔细读样例发现,这里的“独立集”不同于平常的独立集,即不能选中间隔着一个的两个点

    那么对于正常的求解方法是dp[i][0/1]表示当前到i位,选或不选的答案,这里就带限制的dp[i][0/1/2]去进行dp即可,转移是类似的

    对仙人掌的处理方法一样是找环,拆环单独DP

    Code:

    自己的正确版

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    #define maxn 1000100
    struct EdgeNode{int to,next;}edge[maxn<<2];
    int head[maxn],cnt;
    void add(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
    void insert(int u,int v) {add(u,v); add(v,u);}
    int deep[maxn],fa[maxn],dfn[maxn],low[maxn],dp1[maxn][3],dp2[maxn][3],ring[maxn],HX[maxn],t;
    int n,m,ans;
    
    void CactusDP(int st,int tt)
    {
        ring[1]=tt; int zz=1;
        while (ring[zz]!=st) {ring[zz+1]=fa[ring[zz]]; zz++;}
        //printf("Num=%d  :",zz);
        //for (int i=1; i<=zz; i++) printf("%d ->",ring[i]); printf("
    ");
        int f0=0,f1=0,f2=0;
        for (int opt=0; opt<=2; opt++)
            {
                dp2[1][0]=dp2[1][1]=dp2[1][2]=0;
                dp2[1][opt]=dp1[tt][opt];
                if (opt==2) dp2[1][1]=dp2[1][2];
                for (int i=2; i<=zz; i++)
                    dp2[i][0]=dp2[i-1][2]+dp1[ring[i]][0],
                    dp2[i][1]=max(max(dp2[i-1][1],dp2[i-1][0])+dp1[ring[i]][2],dp2[i-1][1]+dp1[ring[i]][1]),
                    dp2[i][2]=max(dp2[i-1][1],dp2[i-1][2])+dp1[ring[i]][2];
                if (opt==0) f1=max(f1,dp2[zz][2]);
                if (opt==1) f1=max(f1,dp2[zz][1]),f2=max(f2,dp2[zz][2]);
                if (opt==2) f1=max(f1,dp2[zz][1]),f0=max(f0,dp2[zz][0]),f2=max(f2,dp2[zz][2]);            
            }
        dp1[st][0]=f0; dp1[st][1]=f1; dp1[st][2]=f2;
    }
    
    void TreeDP(int now)
    {
        dfn[now]=low[now]=++t; 
        dp1[now][2]=0; dp1[now][0]=HX[now]; int maxx=0;
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=fa[now])
                {
                    if (deep[edge[i].to]) {low[now]=min(dfn[edge[i].to],low[now]); continue;}
                    fa[edge[i].to]=now; deep[edge[i].to]=deep[now]+1;
                    TreeDP(edge[i].to);
                    if (low[edge[i].to]>low[now])
                        dp1[now][2]+=max(dp1[edge[i].to][1],dp1[edge[i].to][2]),
                        dp1[now][0]+=dp1[edge[i].to][2],
                        maxx=max(maxx,dp1[edge[i].to][0]-max(dp1[edge[i].to][1],dp1[edge[i].to][2]));
                    low[now]=min(low[now],low[edge[i].to]);
                }
        dp1[now][1]=maxx+dp1[now][2];
        for (int i=head[now]; i; i=edge[i].next)
            if (low[edge[i].to]==dfn[now] && edge[i].to!=fa[now] && deep[edge[i].to]!=deep[now]+1)
                CactusDP(now,edge[i].to);
    }
    
    void Freopen() {freopen("area.in","r",stdin); freopen("area.out","w",stdout);}
    void Fclose() {fclose(stdin); fclose(stdout);}
    
    int main()
    {
        //Freopen();
        n=read(),m=read();
        for (int i=1; i<=n; i++) HX[i]=read();
        for (int u,v,i=1; i<=m; i++) u=read(),v=read(),insert(u,v);
        for (int i=1; i<=n; i++) if (!dfn[i]) {fa[i]=i,deep[i]=1; TreeDP(i); ans+=max(dp1[i][0],max(dp1[i][1],dp1[i][2]));}
        printf("%d
    ",ans);
        //Fclose();
        return 0;
    }
    Area

    错误的标算

    #include <cstdio> 
    #include <cstring> 
    #include <vector> 
    #include <algorithm> 
    using namespace std; 
       
    const int N = 1000500, inf = ~0U >> 1; 
       
    #define forEdges(iter,u) for(edge* iter = e[u]; iter; iter = iter->n) 
    struct edge { int t; edge *n; } 
        ebf[N << 2], *e[N], *ec = ebf; 
       
    int weight[N], n; 
    int dfn[N], low[N], S[N], sTop, dTime; 
    int f[N][3], rf[N][3], id[N], opt[N]; 
       
    inline void updateN (int &x, int y) { if (x > y) x = y; } 
    inline void updateX (int &x, int y) { if (x < y) x = y; } 
       
    inline void dfs (int u, int au) 
    { 
        int v; 
        dfn[u] = low[u] = ++dTime, S[++sTop] = u; 
        forEdges(it, u) if ((v = it->t) != au) 
            if (!dfn[v]) dfs(v, u), updateN(low[u], low[v]); 
            else updateN(low[u], dfn[v]); 
        int maxDelt(0); 
        f[u][2] = 0, f[u][0] = weight[u]; 
        forEdges(it, u) if ((v = it->t) != au && low[v] > dfn[u]) // Sons 
        { 
            f[u][2] += max(f[v][1], f[v][2]); 
            f[u][0] += f[v][2]; 
            updateX(maxDelt, f[v][0] - max(f[v][1], f[v][2])); 
        } 
        f[u][1] = f[u][2] + maxDelt; 
           
        forEdges(it, u) if (low[it->t] == dfn[u]) // A ring 
        { 
            int rs = 0; 
            while (S[sTop] != u) id[++rs] = S[sTop--]; 
            id[++rs] = u; 
            /* RingDP : Line 53 .. 55 */
            int f0 = 0, f1 = 0, f2 = 0; 
            for (int st = 0; st <= 2; ++st) 
            { 
                rf[1][0] = rf[1][1] = rf[1][2] = 0; 
                rf[1][st] = f[id[1]][st]; 
                if (st == 2) rf[1][1] = rf[1][2]; 
                for (int i = 2; i <= rs; ++i) 
                { //RingDP 
                    rf[i][0] = f[id[i]][0] + rf[i - 1][2]; 
                    rf[i][1] = max(f[id[i]][2] + max(rf[i - 1][0], rf[i - 1][1]), f[id[i]][1] + rf[i - 1][1]); 
                    rf[i][2] = f[id[i]][2] + max(rf[i - 1][1], rf[i - 1][2]); 
                } 
                switch (st) 
                { 
                    case 0 : updateX(f1, rf[rs][2]); break; //!! 
                    case 1 : updateX(f1, rf[rs][1]), updateX(f2, rf[rs][2]); break; 
                    case 2 : updateX(f0, rf[rs][0]), updateX(f1, rf[rs][1]), updateX(f2, rf[rs][2]); break; 
                } 
            } 
            f[u][0] = f0, f[u][1] = f1, f[u][2] = f2; 
        } 
           
        if (dfn[u] == low[u]) while (S[sTop + 1] != u) --sTop; 
        opt[u] = max(f[u][0], max(f[u][1], f[u][2])); 
    } 
       
    int main () 
    { 
        int m, a, b; 
       // freopen("area.in", "r", stdin); 
       // freopen("area.out", "w", stdout); 
        scanf("%d%d", &n, &m); 
        for (int i = 1; i <= n; ++i) scanf("%d", weight + i); 
        while (m--) 
        { 
            scanf("%d%d", &a, &b); 
            *ec = (edge){b, e[a]}; e[a] = ec++; 
            *ec = (edge){a, e[b]}; e[b] = ec++; 
        } 
        int res = 0; 
        for (int i = 1; i <= n; ++i) 
            if (!dfn[i]) dfs(i, 0), res += opt[i]; //A Connected Component 
        printf("%d
    ", res); 
        return 0; 
    }
    Area-std

    BZOJ1972猪国杀

    思路:大模拟

    Code:

    迟迟不敢动手
    猪国杀

    BZOJ1974auction代码拍卖会

    思路:DP、组合数学

    发现从左到右每一位不减,那么有个不错的性质,可以组成的数,拆成${1,11,111,1111....}$中取$<=8$个数组合出来

    而这些数%p,最多有p种可能,那么找循环,DP,用组合数计算一下答案即可

    那么方程就是$dp[i][j][k]$表示前i种可能选了j个,组合出来的数%p结果为k的方案数

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define mod 999911659
    long long n;int p,a,ans;
    long long dp[2][1010][1010],inv[1010],c[1010][1010],data[1010],cnt[1010];
    long long C(long long x,int y)
    {
        if (y>x) return 0;
        long long re=1;
        for (long long i=x-y+1; i<=x; i++)
            (re*=(i%mod))%=mod;
        return re*inv[y]%mod;
    }
    void GetInv()
    {
        inv[0]=1,inv[1]=1;
        for (int i=2; i<=9; i++) 
            inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        for (int i=2; i<=9; i++)
            inv[i]=inv[i]*inv[i-1]%mod;
    }
    int main()
    {
        scanf("%lld%d",&n,&p);
        GetInv();
        int x=1%p,sz=0;
        while (!cnt[x]) {cnt[x]=++sz; data[sz]=x; if (sz>=n) break; x=(x*10+1)%p;} 
        if (sz!=n)
            {
                long long N=n-cnt[x]+1; int SZ=sz-cnt[x]+1;
                if (SZ>1) a=(p-data[cnt[x]+(N%SZ?N%SZ:SZ)-1])%p;
                    else a=(p-data[cnt[x]])%p;
                for (int i=0,t=cnt[x]; i<p; i++)
                    if (cnt[i])
                        if (cnt[i]<t) cnt[i]=1;
                            else 
                                if (SZ>1 && (N%SZ)>cnt[i]-t)
                                    cnt[i]=N/SZ+1; else cnt[i]=N/SZ;
            }
        else 
            {
                a=(p-x)%p;
                for (int i=0; i<p; i++) if (cnt[i]) cnt[i]=1;
            }
        for (int i=0; i<p; i++)
            for (int j=0; j<9; j++)
                if (cnt[i]) c[i][j]=C(cnt[i]+j-1,j);
        dp[0][0][0]=1;
        int now=0;
        for (int i=0; i<p; i++)
            if (cnt[i])
                {
                    now^=1;
                    for (int j=0; j<9; j++)
                        for (int k=0; k<p; k++)
                            dp[now][j][k]=dp[now^1][j][k];
                    for (int j=0; j<9; j++)
                        for (int k=0; k<p; k++)
                            if (dp[now^1][j][k])
                                for (int l=1; l<9-j; l++)
                                    (dp[now][j+l][(k+l*i)%p]+=dp[now^1][j][k]*c[i][l]%mod)%=mod;
                }
        for (int i=0; i<9; i++)
            ans=(ans+dp[now][i][a])%mod;
        printf("%d
    ",ans);
        return 0;
    }
    auction代码拍卖会

    BZOJ1975魔法猪学院

    思路:A*求K短路

    大体的思路就是: 首先需要求出每个点到T的最短路径,要求它需要: 
    1.先建出当前图的反图,即每个边的反向边。 
    2.用反向边做一遍SPFA,dis数组存储的即是当前点到T的最短距离; 然后建立估价函数,利用估价函数去维护一个堆,此处使用STL里的Priority_Queue; 
    不断的入队出队,当T出队次数达到K次,即返回值。即为所求K短路;

    会求k短路之后,就每次求k短并相减即可;

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    #define maxm 200010
    #define maxn 5010
    struct data{int to,next;double power;}edge[maxm],reedge[maxm];
    int head[maxn],cnt;int rehead[maxn],recnt;
    int n,m,S,T,ans;
    double po;
    
    void add(int u,int v,double p)
    {
        cnt++;
        edge[cnt].next=head[u]; head[u]=cnt;
        edge[cnt].to=v; edge[cnt].power=p;
        recnt++;
        reedge[recnt].next=rehead[v]; rehead[v]=recnt;
        reedge[recnt].to=u; reedge[recnt].power=p; 
    }
    
    double dis[maxn];
    #define inf 1000000001.0
    inline void spfa()
    {
        queue<int>q;
        bool visit[maxn]; memset(visit,0,sizeof(visit));
        for (int i=S; i<=T; i++) dis[i]=inf;
        q.push(T); dis[T]=0.0; visit[T]=1;
        while (!q.empty())
            {
                int now=q.front(); q.pop();
                for (int i=rehead[now]; i; i=reedge[i].next)
                    if (dis[now]+reedge[i].power<dis[reedge[i].to])
                        {
                            dis[reedge[i].to]=dis[now]+reedge[i].power;
                            if (!visit[reedge[i].to])
                                {
                                    q.push(reedge[i].to);
                                    visit[reedge[i].to]=1;
                                }
                        }
                visit[now]=0;
            }
    }
    
    struct node{
        double g; int v;
        node() {}
        node(double x,int y):g(x),v(y){}
        bool operator < (const node & A) const
            {
                return g+dis[v]>A.g+dis[A.v];
            }
    };
    
    inline void Astar()
    {
        priority_queue<node>Q;
        Q.push(node(0.0,S));
        while(po>0 && !Q.empty()) 
            {
                node cur=Q.top(); Q.pop(); 
                for(int i=head[cur.v]; i; i=edge[i].next)
                    {
                        node A; A.g=edge[i].power+cur.g; A.v=edge[i].to;
                        Q.push(A);
                    }
                if (cur.v==T)
                    {
                        po-=cur.g; if (po<0) return; ans++;
                    }
            }
    }
    
    int main()
    {
        n=read(); m=read(); scanf("%lf",&po);
        for (int i=1; i<=m; i++)
            {
                int u=read(),v=read(); double p;
                scanf("%lf",&p); add(u,v,p);
            }
        S=1; T=n;
        spfa();
        Astar();
        printf("%d
    ",ans);
        return 0;
    }
    魔法猪学院

  • 相关阅读:
    win7 重装 docker 启动后无法启动错误解决
    ASP.NET MVC 播放远程服务器上的MP3文件
    ubuntu+mono+PetaPoco+Oracle+.net 程序部署
    .NET Core 2.0 问题杂记
    博客园挂了吗?
    Content-Type: application/vnd.ms-excel">
    Storm
    Razor语法
    类型后面加问号 int?
    Apache vue site configuration
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5595209.html
Copyright © 2011-2022 走看看