【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\n",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\n",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\n",dis1[i],dis2[i]); printf("%d\n",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\n",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\n",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\n",(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\n",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\n",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\n",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\n",ans); return 0; }
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\n",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("\n"); 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\n",ans); //Fclose(); return 0; }
错误的标算
#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\n", res); return 0; }
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\n",ans); return 0; }
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\n",ans); return 0; }