以后还是这样 25道题一起发 看着爽
noip失利之后发粪涂墙 刷了一波bzoj
题解:
bzoj1000 A+B问题
这题不同的人有不同的写法,我写了个线段树套Treap,应该还是挺简单的
但是看别的大神的代码跑的飞快就粘过来吧 这题竟然不用树套树,太强了
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; int main() { int a,b; cin>>a>>b; cout<<a+b; return 0; }
bzoj1001 狼抓兔子
平面图最小割
建图方式比较奇怪,注意一下就可以了
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int n,m; int ne; struct data{int to,next,v;}e[6000001]; int head[1000001]; int h[1000001],q[1000001],ans; void insert(int u,int v,int w) { ne++; e[ne].to=v; e[ne].v=w; e[ne].next=head[u]; head[u]=ne; } bool bfs() { int now,i; memset(h,-1,sizeof(h)); int t=0,w=1; q[t]=1;h[1]=0; while(t<w) { now=q[t];t++; i=head[now]; while(i) { if(e[i].v&&h[e[i].to]<0) { q[w++]=e[i].to; h[e[i].to]=h[now]+1; } i=e[i].next; } } if(h[n*m]==-1)return 0; return 1; } int dfs(int x,int f) { if(x==n*m)return f; int i=head[x]; int w,used=0; while(i) { if(e[i].v&&h[e[i].to]==h[x]+1) { w=f-used; w=dfs(e[i].to,min(w,e[i].v)); e[i].v-=w; e[i+1].v+=w; used+=w; if(used==f)return f; } i=e[i].next; } if(!used)h[x]=-1; return used; } void dinic() { while(bfs())ans+=dfs(1,0x7fffffff); } int main() { scanf("%d%d",&n,&m); int x; for(int i=1;i<=n;i++) for(int j=1;j<m;j++) { scanf("%d",&x); insert(m*(i-1)+j,m*(i-1)+j+1,x); insert(m*(i-1)+j+1,m*(i-1)+j,x); } for(int i=1;i<n;i++) for(int j=1;j<=m;j++) { scanf("%d",&x); insert(m*(i-1)+j,m*(i)+j,x); insert(m*(i)+j,m*(i-1)+j,x); } for(int i=1;i<n;i++) for(int j=1;j<m;j++) { scanf("%d",&x); insert(m*(i-1)+j,m*(i)+j+1,x); insert(m*(i)+j+1,m*(i-1)+j,x); } dinic(); printf("%d",ans); return 0; }
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<cstdlib> #include<set> #include<map> #include<vector> #include<stack> #include<queue> #define ll long long #define inf 2147383611 #define MAXN 1001001 #define MOD using namespace std; inline int read() { int x=0,f=1; char ch;ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,x,ans,lim; int to[MAXN*6+2010],val[MAXN*6+4010],first[MAXN*2],next[MAXN*6+4010],cnt; int dis[MAXN*2]; bool vis[MAXN*2]; void add(int u,int v,int d) {next[++cnt]=first[u],first[u]=cnt,val[cnt]=d,to[cnt]=v;} void spfa() { memset(dis,127,sizeof(dis)); queue <int> q; q.push(0);dis[0]=0,vis[0]=1; while(!q.empty()) { int k=q.front(); q.pop(); vis[k]=0; for(int i=first[k];i;i=next[i]) if(dis[to[i]]>dis[k]+val[i]) { dis[to[i]]=dis[k]+val[i]; if(!vis[to[i]]) {q.push(to[i]);vis[to[i]]=1;} } } printf("%d",dis[lim+1]); } int main() { n=read(),m=read(); ans=inf,lim=2*(m-1)*(n-1); if(n==1) { for(int i=1;i<m;i++) {x=read(),ans=min(ans,x);} printf("%d",ans);return 0; } if(m==1) { for(int i=1;i<n;i++) {x=read(),ans=min(ans,x);} printf("%d",ans);return 0; } for(int i=1;i<=n;i++) { for(int j=1;j<m;j++) { x=read(); if(i==1) {add(lim+1,j*2,x);add(j*2,lim+1,x);continue;} if(i==n) {add(0,(n-2)*(m-1)*2+j*2-1,x);add((n-2)*(m-1)*2+j*2-1,0,x);continue;} add((i-1)*(m-1)*2+j*2,(i-2)*(m-1)*2+j*2-1,x);add((i-2)*(m-1)*2+j*2-1,(i-1)*(m-1)*2+j*2,x); } } for(int i=1;i<n;i++) { for(int j=1;j<=m;j++) { x=read(); if(j==1) {add(0,(m-1)*(i-1)*2+1,x);add(2*(m-1)*(i-1)+1,0,x);continue;} if(j==m) {add(lim+1,i*(m-1)*2,x);add(i*(m-1)*2,lim+1,x);continue;} add((i-1)*(m-1)*2+2*(j-1),(i-1)*(m-1)*2+2*j-1,x);add((i-1)*(m-1)*2+2*j-1,(i-1)*(m-1)*2+2*(j-1),x); } } for(int i=1;i<n;i++) { for(int j=1;j<m;j++) { x=read(); add((i-1)*(m-1)*2+2*j,(i-1)*(m-1)*2+2*j-1,x);add((i-1)*(m-1)*2+2*j-1,(i-1)*(m-1)*2+2*j,x); } } spfa(); }
bzoj1002 轮状病毒
这题当年应该没多少人严格论证并写出来正解,时至今日已成为一道高精度练习题
网上严格论证。。。稀如凤毛麟角
找规律+高精度
#include<iostream> #include<cstdio> using namespace std; struct data{ int a[101],len; }; int n; data mul(data a,int k) { for(int i=1;i<=a.len;i++) a.a[i]*=k; for(int i=1;i<=a.len;i++) { a.a[i+1]+=a.a[i]/10; a.a[i]%=10; } if(a.a[a.len+1]!=0)a.len++; return a; } data sub(data a,data b) { a.a[1]+=2; int j=1; while(a.a[j]>=10){a.a[j]%=10;a.a[j+1]++;j++;} for(int i=1;i<=a.len;i++) { a.a[i]-=b.a[i]; if(a.a[i]<0){a.a[i]+=10;a.a[i+1]--;} } while(a.a[a.len]==0)a.len--; return a; } int main() { data f[101];f[1].a[1]=1;f[2].a[1]=5; f[1].len=f[2].len=1; scanf("%d",&n); for(int i=3;i<=n;i++) f[i]=sub(mul(f[i-1],3),f[i-2]); for(int i=f[n].len;i>0;i--) printf("%d",f[n].a[i]); return 0; }
bzoj1003 物流运输
spfa+dp,dp[i][j]表示从第i天到第j天表示从第i天到第j天都能走的1~n最短路长度
f[i]表示前i天最小费用,初始化f[i]=dp[1][i]
递推式为f[i]=min{f[j]+dp[j+1][i]+k}(i>=2,j∈[1,i-1])
bzoj1004 Cards
置换群 Burnside定理 背包
反正就是个狂拽酷炫吊炸天的背包
具体可以看https://www.cnblogs.com/JoeFan/p/4299628.html这篇文章讲的挺好的
心疼当年考的小伙伴
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 110 using namespace std; typedef long long ll; int sr,sb,sg,n,m,mod; int cir[N][N],v[N],size[N]; ll f[N][N][N],ans; int calc(int x) { int cnt=0; memset(v,0,sizeof(v)); memset(size,0,sizeof(size)); for(int i=1;i<=n;i++) { if(v[i])continue; int p=cir[x][i]; cnt++; while(!v[p])v[p]=1,size[cnt]++,p=cir[x][p]; } memset(f,0,sizeof(f)); f[0][0][0]=1; for(int i=1;i<=cnt;i++) for(int j=sr;j>=0;j--) for(int k=sb;k>=0;k--) for(int l=sg;l>=0;l--) { if(j>=size[i])f[j][k][l]=(f[j][k][l]+f[j-size[i]][k][l])%mod; if(k>=size[i])f[j][k][l]=(f[j][k][l]+f[j][k-size[i]][l])%mod; if(l>=size[i])f[j][k][l]=(f[j][k][l]+f[j][k][l-size[i]])%mod; } return f[sr][sb][sg]; } ll quick_my(ll x,int y) { ll ret=1; while(y) { if(y&1)ret=(ret*x)%mod; x=(x*x)%mod; y>>=1; } return ret; } int main() { scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&mod); n=sr+sb+sg; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) scanf("%d",&cir[i][j]); m++; for(int i=1;i<=n;i++)cir[m][i]=i; ll ans=0; for(int i=1;i<=m;i++)ans=(ans+calc(i))%mod; ans=(ans*quick_my(m,mod-2))%mod; printf("%lld ",ans); }
bzoj1005 明明的烦恼
又是一道结论题
#include <cstdio> #include <cmath> #include <cstring> struct bignum { int l, a[10000]; bignum (int t) { l = 0; memset(a, 0, sizeof(0)); while (t != 0) { a[++l] = t % 10000; t /= 10000; } if (l == 0) l = 1; } void mul(int k) { for (int i = 1; i <= l; i++) a[i] *= k; for (int i = 1; i <= l; i++) if (a[i] >= 10000) { int t = i; do { a[t + 1] += a[t] / 10000; a[t++] %= 10000; } while (a[t] >= 10000); } while (a[l + 1] > 0) l++; } void print() { printf("%d", a[l]); for (int i = l - 1; i >= 1; i--) printf("%04d", a[i]); printf(" "); } }; int n, m = 0, tot = 0, d[1001], prime[1001], cnt[1001]; bool judge(int k) { for (int i = 2; i <= sqrt(k); i++) if (k % i == 0) return false; return true; } void makelist(int n) { prime[0] = 0; for (int i = 2; i <= n; i++) if (judge(i)) prime[++prime[0]] = i; } void compute(int k, int t) { for (int i = 1; i <= prime[0] && prime[i] <= k; i++) { int x = 0, n = k, p = prime[i]; while (n != 0) { x += n / p; n /= p; } cnt[i] += x * t; } } int main() { //freopen("input.txt", "r", stdin); //freopen("output.txt", "w", stdout); scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &d[i]); if (d[i] == -1) m++; else tot += d[i] - 1; } makelist(n); memset(cnt, 0, sizeof(cnt)); compute(n - 2, 1); compute(n - 2 - tot, -1); for (int i = 1; i <= n; i++) if (d[i] != -1) compute(d[i] - 1, -1); bignum ans = 1; for (int i = 1; i <= prime[0]; i++) for (int j = 1; j <= cnt[i]; j++) ans.mul(prime[i]); for (int i = 1; i <= n - 2 - tot; i++) ans.mul(m); ans.print(); return 0; }
prufer序列的相关知识可以见https://www.cnblogs.com/CSU3901130321/p/4898261.html这篇博客
bzoj1006 神奇的国度
简单弦图的染色
可以见CDQ的论文
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #define inf 0x7fffffff #define ll long long using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,cnt,ans; int head[10005],d[10005],q[10005],col[10005],hash[10005]; bool vis[10005]; struct data{int to,next;}e[2000005]; void ins(int u,int v) {e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;} int main() { n=read();m=read(); for(int i=1;i<=m;i++) { int u=read(),v=read(); ins(u,v);ins(v,u); } for(int i=n;i;i--) { int t=0; for(int j=1;j<=n;j++) { if(!vis[j]&&d[j]>=d[t])t=j; } vis[t]=1;q[i]=t; for(int j=head[t];j;j=e[j].next) d[e[j].to]++; } for(int i=n;i>0;i--) { int t=q[i]; for(int j=head[t];j;j=e[j].next)hash[col[e[j].to]]=i; int j; for(j=1;;j++)if(hash[j]!=i)break; col[t]=j; if(j>ans)ans=j; } printf("%d",ans); return 0; }
bzoj1007 水平可见直线
终于不是结论题了
水题 离散化 单调栈 完事
#include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib> #include<cmath> #include<cstring> using namespace std; const double eps=1e-7; struct Vec{double a,b;int rank;}lines[50010]; Vec st[50010];int top; bool cmp1(Vec x,Vec y) { if(fabs(x.a-y.a)<eps)return x.b<y.b; return x.a<y.a; } int check[50010]; double Sv(Vec a,Vec b){return (b.b-a.b)/(a.a-b.a);} void Push(Vec a) { while(top) { if(fabs(st[top].a-a.a)<eps)--top; else if(top>1 && Sv(a,st[top-1])<=Sv(st[top],st[top-1]))--top; else break; } st[++top]=a; } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++){scanf("%lf%lf",&lines[i].a,&lines[i].b);lines[i].rank=i;} sort(lines+1,lines+n+1,cmp1); for(int i=1;i<=n;i++)Push(lines[i]); for(int i=1;i<=top;i++)check[st[i].rank]=1; for(int i=1;i<=n;i++)if(check[i])cout<<i<<" "; return 0; }
bzoj1008 越狱
数学题 要时刻注意%还是不%,%完了是正的负的
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #define mod 100003 #define ll long long using namespace std; ll m,n; ll qpow(ll a,ll b) { ll c=1,d=a%mod; while (b>0) { if (b&1) c=(c%mod*d%mod)%mod; b>>=1; d=(d%mod*d%mod)%mod; } return c; } int main() { scanf("%lld%lld",&m,&n); long long ans=qpow(m,n); ans=ans-m*qpow(m-1,n-1)%mod; if (ans<0) ans+=mod; printf("%lld",ans); return 0; }
bzoj1009 GT考试
KMP+矩阵乘法加速DP
递推式很好想
设a[k][j]为k位后面加一个字母转移到j的方案数
dp[i][j]=∑dp[i−1][k]∗a[k][j](0<=k<=m−1)
因为是线性的 可以矩阵优化
#include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib> #include<cmath> #include<cstring> #define ll long long int m,mod; using namespace std; struct mat { ll a[25][25]; mat operator*(const mat &y)const { mat res; memset(res.a,0,sizeof(res.a)); for(int i=0;i<m;i++) for(int j=0;j<m;j++) for(int k=0;k<m;k++) { res.a[i][j]+=a[i][k]*y.a[k][j]; res.a[i][j]%=mod; } return res; } }a,b; mat ksm(mat A,int n) { mat res; if(n==1) return A; res=ksm(A,n/2); res=res*res; if(n%2==1)res=res*A; return res; } void mul(int a[25][25],int b[25][25],int ans[25][25]) { int tmp[25][25]; for(int i=0;i<m;i++) for(int j=0;j<m;j++) { tmp[i][j]=0; for(int k=0;k<m;k++) tmp[i][j]=(tmp[i][j]+a[i][k]*b[k][j])%mod; } for(int i=0;i<m;i++) for(int j=0;j<m;j++) ans[i][j]=tmp[i][j]; } int sum; char ch[30010]; int nxt[30010]; int main() { int n; scanf("%d%d%d",&n,&m,&mod); scanf("%s",ch+1); int j=0; for(int i=2;i<=m;i++) { for(;j>0 && ch[j+1]!=ch[i];j=nxt[j]); if(ch[j+1]==ch[i])j++; nxt[i]=j; } for(int i=0;i<m;i++) for(int j=0;j<=9;j++) { int t=i; for(;t>0&&ch[t+1]-'0'!=j;t=nxt[t]); if(ch[t+1]-'0'==j)t++; if(t!=m)b.a[t][i]=(b.a[t][i]+1)%mod; } for(int i=0;i<m;i++)a.a[i][i]=1; while(n) { if(n&1)a=a*b; b=b*b; n>>=1; } for(int i=0;i<m;i++)(sum+=a.a[i][0])%=mod; printf("%d",sum); return 0; }
bzoj1010 玩具装箱toy
斜率优化
dp[i]=min(dp[j]+(sum[i]-sum[j]+i-j-1-L)^2) (j<i)
推一推 搞成令f[i]=sum[i]+i,c=1+l
然后用一个单调队列维护下凸壳就好了
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<cstdlib> #define ll long long using namespace std; int n,L,l,r; int c[50005],q[50005]; ll s[50005],f[50005]; double slop(int j,int k){return (f[k]-f[j]+(s[k]+L)*(s[k]+L)-(s[j]+L)*(s[j]+L))/(2.0*(s[k]-s[j]));} int main() { scanf("%d%d",&n,&L);L++; for(int i=1;i<=n;i++)scanf("%d",&c[i]); for(int i=1;i<=n;i++)s[i]=s[i-1]+c[i]; for(int i=1;i<=n;i++)s[i]+=i; l=1;r=0;q[++r]=0; for(int i=1;i<=n;i++) { while(l<r && slop(q[l],q[l+1])<=s[i])l++; int t=q[l]; f[i]=f[t]+(s[i]-s[t]-L)*(s[i]-s[t]-L); while(l<r && slop(q[r],i)<slop(q[r-1],q[r]))r--; q[++r]=i; } printf("%lld ",f[n]); return 0; }
bzoj1011 遥远的行星
puts("-nan");
果然HNaNOI
正经做也是可以的
由于“误差不超过5%”
当数据小于50时,暴力 大于50时,用到一些估算技巧
没有做过这么奇怪的题呢
#include <bits/stdc++.h> using namespace std; double m[100005], sum[100005]; int main() { int n; double a, ans; scanf("%d%lf", &n, &a); for(int j = 1; j <= n; ++j) { int i = (int)(a * j + 1e-8); scanf("%lf", m + j); ans = 0; if(j <= 500) for(int k = 1; k <= i; ++k) ans += m[k] * m[j] / (j - k); else ans = sum[i] * m[j] / (j - i / 2); printf("%f ", ans); sum[j] = sum[j - 1] + m[j]; } return 0; }
bzoj1012 最大数
单调队列,二分查找
但其实单调栈也是可以的。。。
看到网上一片线段树的时候我以为我做错了
#include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; int n,m,last,mod; int q[200005],id[200005],head,tail; void add(int x) { while (q[tail]<=x && tail) tail--; q[++tail]=x;id[tail]=++n; } int query(int x) { int l=n-x+1; int k=lower_bound(id+head,id+tail+1,l)-id; return q[k]; } int main() { scanf("%d%d",&m,&mod); char ch[2]; int x; head=1;tail=0; while(m--) { scanf("%s%d",ch,&x); if(ch[0]=='A') add((x+last)%mod); else printf("%d ",last=query(x)); } return 0; }
bzoj1013 Sphere(名字太长辣)
高斯消元法裸题
但是推方程组用了我3张纸
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> using namespace std; #define zero 1e-6 int n; double ans[15],a[15][15],d[15][15]; void init() { cin>>n; for (int i=1; i<=n+1; i++) for (int j=1; j<=n; j++) cin>>d[i][j]; for (int i=1; i<=n; i++) { for (int j=1; j<=n; j++) a[i][j] = 2 * (d[i+1][j]-d[i][j]), a[i][n+1] += d[i+1][j] * d[i+1][j] - d[i][j] * d[i][j]; } memset(ans,0,sizeof ans); } void gauss() { for (int i=1; i<n; i++) { if (fabs(a[i][i])<zero) for (int j=i+1; j<=n; j++) if (abs(a[j][i])>zero) { for (int k=1; k<=n+1; k++) swap(a[i][k],a[j][k]); break; } for (int j=i+1; j<=n; j++) { double x = a[j][i] / a[i][i]; for (int k=i; k<=n+1; k++) a[j][k] -= a[i][k] * x; } } ans[n]=a[n][n+1]/a[n][n]; for (int i=n-1; i; i--) { for (int j=i+1; j<=n; j++) a[i][n+1] -= ans[j]*a[i][j]; ans[i] = a[i][n+1] / a[i][i]; } } void print() { for (int i=1; i<n; i++) printf("%.3lf ",ans[i]); printf("%.3lf ",ans[n]); } int main() { init(); gauss(); print(); return 0; }
bzoj1014 火星人
Splay,我写了题解
传送门:http://www.cnblogs.com/Kong-Ruo/p/7895957.html
bzoj1015 星球大战
并查集,操作离线
不得不说脑洞挺大的
将操作倒过来
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int fa[400010]; inline int read() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9'){ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x; } int n,m; int u,v,atk[400010],d[400010],ans[400010],vis[400010]; inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} int first[400010],to[400010],next[400010],cnt=1; void add(int u,int v){to[++cnt]=v;next[cnt]=first[u];first[u]=cnt;} int tmp; void destroy(int x) { int p=find(x),q; for(int i=first[x];i;i=next[i]) { if(!vis[to[i]])continue; q=find(to[i]); if(p!=q){fa[q]=p;tmp--;} } } int main() { n=read();m=read(); for(int i=0;i<n;i++)fa[i]=i; for(int i=1;i<=m;i++){u=read(),v=read();add(u,v);add(v,u);} int k=read(); for(int i=1;i<=k;i++){atk[i]=read();d[atk[i]]=1;} for(int i=0;i<n;i++) { if(!d[i]) { tmp++; destroy(i); vis[i]=1; } } ans[k+1]=tmp; for(int i=k;i>0;i--) { tmp++; destroy(atk[i]); vis[atk[i]]=1; ans[i]=tmp; } for(int i=1;i<=k+1;i++)printf("%d ",ans[i]); return 0; }
bzoj1016 最小生成树计数
Kruskal+乘法原理
另:千万不要一看到并查集就写路径压缩!!!
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int mod=31011; int fa[105]; struct edge{int x,y,v;}e[1005]; struct EDG{int l,r,v;}a[1005]; inline int read() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9'){ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x; } int n,m,cnt,cntt,ans,sum; bool cmp(edge a,edge b){return a.v<b.v;} inline int find(int x){return x==fa[x]?x:find(fa[x]);} //这里不能路径压缩!!!WA了好几遍 void kruskal() { sort(e+1,e+m+1,cmp); for(int i=1;i<=m;i++) { if(e[i].v!=e[i-1].v){a[++cnt].l=i;a[cnt-1].r=i-1;} int p=find(e[i].x),q=find(e[i].y); if(p!=q){fa[p]=q;a[cnt].v++;cntt++;} } a[cnt].r=m; } void dfs(int x,int c,int k) { if(c==a[x].r+1) { if(k==a[x].v)sum++; return; } int p=find(e[c].x),q=find(e[c].y); if(p!=q) { fa[p]=q; dfs(x,c+1,k+1); fa[p]=p;fa[q]=q; } dfs(x,c+1,k); } void kkruskal() { for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=cnt;i++) { sum=0; dfs(i,a[i].l,0); ans=(ans*sum)%mod; for(int j=a[i].l;j<=a[i].r;j++) { int p=find(e[j].x),q=find(e[j].y); if(p!=q)fa[p]=q; } } } int main() { ans++; n=read();m=read(); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=m;i++)e[i].x=read(),e[i].y=read(),e[i].v=read(); kruskal(); if(cntt!=n-1){printf("0");return 0;} kkruskal(); printf("%d",ans); return 0; }
bzoj1017 魔兽地图DotR
斗地主加强版
装备合成形成一个森林
预处理出高级物品的力量,价格,购买上限
用f[i][j][k]表示前i个物品 有j个用于合成 总花费为k时提供的力量最大值
对于以a为根的子树枚举合成b个物品,再把剩下的钱买不用于合成的物品
用ff[i][j]表示以a为根的子树里的前i个节点花费为j时可以获得的力量最大值
再d一遍
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<cmath> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=10*x+f-'0';ch=getchar();} return x*f; } const int maxn=60; const int inf=2147483233; int dp[maxn][maxn*2][2050],c[maxn][maxn*40],f[maxn][maxn*40]; int n,m,t; int lim[maxn],pri[maxn],p[maxn]; int rd[maxn]; struct EDG { int to,next,val; }e[maxn*maxn*40]; int first[maxn],cnt; inline void add(int u,int v,int w) { e[++cnt].to=v; e[cnt].next=first[u]; e[cnt].val=w; first[u]=cnt; rd[v]++; } char ch[5]; void dfs(int x) { if(!first[x]) { lim[x]=min(lim[x],m/pri[x]); for(int i=0;i<=lim[x];i++) for(int j=i;j<=lim[x];j++) dp[x][i][j*pri[x]]=(j-i)*p[x]; return; } lim[x]=2147483233; for(int i=first[x];i;i=e[i].next) { dfs(e[i].to); lim[x]=min(lim[x],lim[e[i].to]/e[i].val); pri[x]+=e[i].val*pri[e[i].to]; } lim[x]=min(lim[x],m/pri[x]); memset(c,-0x3f3f3f3f,sizeof(c)); c[0][0]=0; for(int i=lim[x];i>=0;i--) { int cc=0; for(int j=first[x];j;j=e[j].next) { cc++; for(int k=0;k<=m;k++) for(int l=0;l<=k;l++) c[cc][k]=max(c[cc][k],c[cc-1][k-l]+dp[e[j].to][i*e[j].val][l]); } for(int j=0;j<=i;j++) for(int k=0;k<=m;k++) dp[x][j][k]=max(dp[x][j][k],c[cc][k]+p[x]*(i-j)); } } int main() { memset(dp,-0x3f3f3f3f,sizeof(dp)); scanf("%d%d",&n,&m); int x,y,uu; for(int i=1;i<=n;i++) { scanf("%d%d",&p[i]); scanf("%s",ch); if(ch[0]=='B')scanf("%d%d",&pri[i],&lim[i]); else { scanf("%d",&x); for(int j=1;j<=x;j++) { scanf("%d%d",&y,&uu); add(i,y,uu); } } } for(int z=1;z<=n;z++) { if(!rd[z]) { dfs(z); t++; for(int i=1;i<=m;i++) for(int j=0;j<=i;j++) for(int k=0;k<=lim[z];k++) f[t][i]=max(f[t][i],f[t-1][j]+dp[z][k][i-j]); } } int ans=-2147483233; for(int i=0;i<=m;i++)ans=max(ans,f[t][i]); printf("%d ",ans); }
bzoj1008 堵塞的交通Traffic
本来以为线段树都是慈眉善目的
线段树每个节点记一个2*2矩阵中6组连通性
走的时候左边的节点先走到最左边,右边节点先走到最右边
然后往中间走
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #define l(x) (x<<1) #define r(x) ((x<<1)|1) using namespace std; const int maxn=800010; char opt[15]; int x1,y1,x2,y2,c; struct Segtree { struct Martix { int a1[2][2]; int a2[2]; }; struct Treenode { int l,r; Martix s; }tr[maxn]; int bl[maxn][2]; Martix upd(Martix s1,Martix s2,int b[]) { Martix res; for(int i=0;i<=1;i++) for(int j=0;j<=1;j++) res.a1[i][j]=s1.a1[i][0] && b[0] && s2.a1[0][j] || s1.a1[i][1] && b[1] && s2.a1[1][j]; res.a2[0]=s1.a2[0] || s1.a1[0][0] && b[0] && s2.a2[0] && b[1] && s1.a1[1][1]; res.a2[1]=s2.a2[1] || s2.a1[0][0] && b[0] && s1.a2[1] && b[1] && s2.a1[1][1]; return res; } Martix getc(int id,int l,int r) { int mid=(tr[id].l+tr[id].r)>>1; if(l<=tr[id].l && r>=tr[id].r) return tr[id].s; else if(l>mid)return getc(r(id),l,r); else if(r<=mid)return getc(l(id),l,r); else return upd(getc(l(id),l,r),getc(r(id),l,r),bl[id]); } void update(bool f,int id,int sx,int sy,int gx,int gy) { int mid=(tr[id].l+tr[id].r)>>1; if(sx==gx && sy==mid) { bl[id][sx]=f; tr[id].s=upd(tr[l(id)].s,tr[r(id)].s,bl[id]); } else if(tr[id].l==tr[id].r) tr[id].s.a1[0][1]=tr[id].s.a1[1][0]=tr[id].s.a2[0]=tr[id].s.a2[1]=f; else { if(gy<=mid)update(f,l(id),sx,sy,gx,gy); else update(f,r(id),sx,sy,gx,gy); tr[id].s=upd(tr[l(id)].s,tr[r(id)].s,bl[id]); } } void build(int id,int l,int r) { tr[id].l=l;tr[id].r=r; if(l==r) { tr[id].s.a1[0][0]=tr[id].s.a1[1][1]=1; return; } int mid=(l+r)>>1; build(l(id),l,mid); build(r(id),mid+1,r); } void query(int sx,int sy,int gx,int gy) { Martix L=getc(1,1,sy),Mid=getc(1,sy,gy),R=getc(1,gy,c); int res=0; for(int i=0;i<=1;i++) for(int j=0;j<=1;j++) if(Mid.a1[i][j]&&(i==sx||L.a2[1])&&(j==gx||R.a2[0])){res=1;break;} if(res)printf("Y "); else printf("N "); } }seg; int main() { scanf("%d",&c); seg.build(1,1,c); while(1) { scanf("%s",opt); if(opt[0]=='E') break; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); --x1; --x2; if(y1>y2) { swap(x1,x2); swap(y1,y2); } if(opt[0]=='O') seg.update(1,1,x1,y1,x2,y2); else if(opt[0]=='C') seg.update(0,1,x1,y1,x2,y2); else seg.query(x1,y1,x2,y2); } return 0; }
bzoj1019 汉诺塔
一个经典的汉诺塔型递推
结合汉诺塔&&BLAH,可想到递推方程: f[x][i] ,g[x][i] 分别表示,当前x柱上有i个圆盘,将它们移至任意另一个柱的操作数,和另一柱子的编号.(如上,这显然对任意连续的i个圆盘,都是唯一固定的)
f[x][i],g[x][i] 可由 f[][i-1],g[][i-1] 推得:
汉诺塔的经典转移,先做子问题把x柱上的i-1个圆盘移走,再把第i个大圆盘移走..
若设y=g[x][i-1],z=1+2+3-y-x(即除x,y以外的柱子编号)
即1) x上i-1个圆盘移至y上
2)由于不能对一个圆盘进行重复操作,所以必是将x上的第i个圆盘,移至z
由于i个圆盘还没叠到一起,所以接下来显然还要再次移动y上的i-1个,这时需要分类讨论:
若 f[y][i-1]=z:
3)移到z后,便结束了
综合以上,这种情况下,f[x][i]=f[x][i-1]+1+f[y][i-1],g[x][i]=z
若f[y][i-1]=x:
3)i-1个圆盘移至x
4)不能对一个圆盘进行重复操作,所以必将z上的第i个圆盘,移至y
5)因g[x][i-1]=y,所以x上i-1个圆盘移至y,结束
综合以上,这种情况下,f[x][i]=f[x][i-1]+1+f[y][i-1]+1+f[x][i-1],g[x][i]=y
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define LL long long using namespace std; int v[5],n,g[5][50]; LL f[5][50]; int main() { scanf("%d",&n); for (int i=1;i<=6;i++) { char s[5]; scanf("%s",s); int from=s[0]-'A'+1,to=s[1]-'A'+1; if (v[from]) continue; v[from]=1; g[from][1]=to,f[from][1]=1; } for (int i=2;i<=n;i++) for (int j=1;j<=3;j++) { int y=g[j][i-1]; int z=6-y-j; f[j][i]=f[j][i-1]+1; if (z==g[y][i-1]) { f[j][i]+=f[y][i-1]; g[j][i]=z; } else { f[j][i]+=f[y][i-1]+1+f[j][i-1]; g[j][i]=y; } } cout<<f[1][n]<<endl; return 0; }
bzoj1020 安全的航线
看着莫队的论文战战兢兢的做完这道题
计算几何入门题
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const double eps=1e-16; const int MAXQ=1000000; int n,m;double ans; int dcmp(double a) { if(fabs(a)<eps)return 0; else return a>0?1:-1; } struct Vector { double x,y; Vector operator +(const Vector &a)const{return (Vector){x+a.x,y+a.y};} Vector operator -(const Vector &a)const{return (Vector){x-a.x,y-a.y};} bool operator ==(const Vector &a)const{return dcmp(x-a.x)==0 && dcmp(y-a.y)==0;} Vector operator *(const double &a)const{return (Vector){x*a,y*a};} Vector operator /(const double &a)const{return (Vector){x/a,y/a};} void Read(){scanf("%lf %lf",&x,&y);} }temp[80]; double dot(Vector a,Vector b){return a.x*b.x+a.y*b.y;} double Length(Vector a){return sqrt(dot(a,a));} double Angle(Vector a,Vector b){return acos(dot(a,b)/Length(a)/Length(b));} double Xmult(Vector a,Vector b){return a.x*b.y-b.x*a.y;} double Area(Vector a,Vector b,Vector c){return Xmult(b-a,c-a);} int Intersec(Vector a,Vector b,Vector c,Vector d) { if(max(a.x,b.x)<min(c.x,d.x))return false; if(max(a.y,b.y)<min(c.y,d.y))return false; if(max(c.x,d.x)<min(a.x,b.x))return false; if(max(c.y,d.y)<min(a.y,b.y))return false; if(Area(c,b,a)*Area(b,d,a)<0)return false; if(Area(a,d,c)*Area(d,b,c)<0)return false; return true; } Vector Interpoint(Vector a,Vector b,Vector c,Vector d) { Vector u=a-c; double t=Xmult(d,u)/Xmult(b,d); return a+b*t; } Vector Normal(Vector a){return (Vector){-a.y,a.x};} double Distoline(Vector p,Vector a,Vector b) { Vector v1=b-a,v2=p-a; return fabs(Xmult(v1,v2)/Length(v1)); } int Onseg(Vector p,Vector a,Vector b){return dcmp(Xmult(a-p,b-p))==0 && dcmp(dot(a-p,b-p))<0;} #define point Vector struct Seg{point a,b;}que[1000010]; struct polygon { point ps[50]; int cnt; inline int inpo(point &a) { int tot=0; for(int i=1;i<=cnt;i++) if(Onseg(a,ps[i],ps[i%cnt+1]))return 1; point ray=(point){-10001,a.y+0.1}; a.y+=0.1; for(int i=1;i<=cnt;i++) tot+=Intersec(ray,a,ps[i],ps[i%cnt+1]); a.y-=0.1; return tot&1; } }island[50]; struct near { point p; double dis; }; near disps(point a,point b,point c) { if(b==c)return (near){b,Length(b-a)}; Vector v1=c-b,v2=a-b,v3=a-c; if(dcmp(dot(v1,v2))<=0) return (near){b,Length(v2)}; if(dcmp(dot(v1,v3))>=0) return (near){c,Length(v3)}; Vector v=Normal(b-c); point ans=Interpoint(a,v,b,v1); return (near){ans,Length(a-ans)}; } bool check(point a) { for(int i=1;i<=n;i++) if(island[i].inpo(a)) return true; return false; } near Find(point &p) { if(check(p)) return (near){p,0}; near ans1; ans1.dis=1<<30; for(int i=1;i<=n;i++) for(int j=1;j<=island[i].cnt;j++) { near get=disps(p,island[i].ps[j],island[i].ps[j%island[i].cnt+1]); if(dcmp(ans1.dis-get.dis)>=0) ans1=get; } ans=max(ans,ans1.dis); return ans1; } void dfs() { int front=0,rear=0; for(int i=1;i<m;i++) que[++rear]=(Seg){temp[i],temp[i+1]},Find(temp[i]); Find(temp[m]); Seg head; while(front!=rear) { head=que[front=front%MAXQ+1]; point p1=Find(head.a).p,p2=Find(head.b).p,l=head.a,r=head.b,mid=(l+r)/2; while(Length(r-l)>1e-4) { point mid=(r+l)/2; if(Length(mid-p1)<Length(mid-p2)) l=mid; else r=mid; } double nowans=max(Length(l-p1),Length(l-p2)); Find(l); if(ans+0.005<nowans) que[rear=rear%MAXQ+1]=(Seg){head.a,mid},que[rear=rear%MAXQ+1]=(Seg){mid,head.b}; } } int main() { scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) temp[i].Read(); for(int i=1;i<=n;i++) { scanf("%d",&island[i].cnt); for(int j=1;j<=island[i].cnt;j++) island[i].ps[j].Read(); } dfs(); printf("%.2lf",ans); }
bzoj1021 循环的债务
dp[i][j][k]表示第i种钱,第一个人还有j块钱,第二个人还有k块钱时的交换钞票数
然后依然是个背包
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<cmath> using namespace std; int x1,x2,x3; int cnt[10][10],sum[10]; const int m[]={0,100,50,20,10,5,1}; int f[10][1200][1200]; int g[20]; int tmp[5]; int gcd(int a,int b){return b?gcd(b,a%b):a;} int main() { scanf("%d%d%d",&x1,&x2,&x3); for(int i=0;i<=2;i++) for(int j=1;j<=6;j++) { scanf("%d",&cnt[i][j]); sum[i]=sum[i]+cnt[i][j]*m[j]; } int r1=sum[0]-x1+x3,r2=sum[1]-x2+x1; g[1]=100; for(int i=2;i<=6;i++)g[i]=gcd(g[i-1],m[i]); memset(f,0x3f,sizeof(f)); f[6][sum[0]][sum[1]]=0; for(int i=6;i>=1;i--) { int j=sum[1]+sum[2]+sum[0]; while((j-r1)%g[i])j--; for(;j>=0;j-=g[i]) { int k=sum[1]+sum[2]+sum[0]-j; while((k-r2)%g[i])k--; for(;k>=0;k-=g[i]) { if(f[i][j][k]>2000)continue; f[i-1][j][k]=min(f[i-1][j][k],f[i][j][k]); for(int x=0;x<=2;x++) { int y=(x+1)%3,z=(x+2)%3; for(int xx=0;xx<=cnt[x][i];xx++) for(int yy=0;yy<=xx;yy++) { tmp[x]=-xx*m[i];tmp[y]=yy*m[i];z[tmp]=(xx-yy)*i[m]; f[i-1][j+tmp[0]][k+tmp[1]]=min(f[i-1][j+tmp[0]][k+tmp[1]],f[i][j][k]+xx); } //Invisible w for(int xx=0;xx<=cnt[y][i];xx++) for(int yy=0;yy<=cnt[z][i];yy++) { tmp[x]=(xx+yy)*m[i]; tmp[y]=-xx*m[i];tmp[z]=-yy*m[i]; f[i-1][j+tmp[0]][k+tmp[1]]=min(f[i-1][j+tmp[0]][k+tmp[1]],f[i][j][k]+xx+yy); } } } } } int ans=f[0][r1][r2]; printf(ans>=2000?"impossible":"%d",ans); return 0; }
bzoj1022 小约翰的游戏
一看这不是Nim吗然后交了个Nim上去
发现竟然跟Nim是相反的
然后出现了一个新的小细节:如果有偶数堆,每堆一个石子,先手会赢
博弈论的题竟然不是Alice和Bob,好评
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; int x,sum,flag; int T,n; int main() { scanf("%d",&T); while(T--) { sum=0,flag=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); sum^=x; if(x!=1)flag=1; } if((sum==0 && flag==0) || (sum!=0 && flag==1))printf("John "); else printf("Brother "); } return 0; }
bzoj1023 仙人掌图
很大意义上来说这不算仙人掌的题
搞成一棵树找直径
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; int first[500100],to[1000100],next[1000100],fa[500100],cnt; int ind,dfn[500100],low[500100],depth[500100],q[1000100]; int a[500100]; int ans; int f[500100]; int n,m; inline int read() { int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-f; for(;isdigit(ch);ch=getchar())x=10*x+ch-'0'; return x*f; } inline void add(int u,int v) { to[++cnt]=v; next[cnt]=first[u]; first[u]=cnt; } inline void dp(int x,int y) { int cnt=depth[y]-depth[x]+1,head=1,tail=1,i; for (i=y; i!=x; i=fa[i]) a[cnt--]=f[i]; a[1]=f[x]; cnt=depth[y]-depth[x]+1; q[1]=1; for (i=1; i<=cnt; i++) a[i+cnt]=a[i]; for (i=2; i<=cnt+(cnt>>1); i++){ if (i-q[head]>(cnt>>1)) head++; ans=max(ans,a[i]+i+a[q[head]]-q[head]); while (head<=tail && a[i]-i>=a[q[tail]]-q[tail]) tail--; q[++tail]=i; } for (i=2; i<=cnt; i++) f[x]=max(f[x],a[i]+min(i-1,cnt-i+1)); } inline void Tarjan_dfs(int x) { dfn[x]=low[x]=++ind; for(int i=first[x];i;i=next[i]) { if(to[i]==fa[x])continue; if(!dfn[to[i]]) { fa[to[i]]=x; depth[to[i]]=depth[x]+1; Tarjan_dfs(to[i]); } low[x]=min(low[x],low[to[i]]); if(low[to[i]]>dfn[x]) { ans=max(ans,f[to[i]]+f[x]+1); f[x]=max(f[x],f[to[i]]+1); } } for(int i=first[x];i;i=next[i]) if(fa[to[i]]!=x && dfn[x]<dfn[to[i]])dp(x,to[i]); } int main() { n=read(); m=read(); int i; for (i=1; i<=m; i++) { int tmp=read(),last=0; while (tmp--){ int x=read(); if (last){ add(x,last); add(last,x); } last=x; } } Tarjan_dfs(1); printf("%d ",ans); return 0; }
bzoj1024 生日快乐
爆搜,每次把蛋糕砍成两块
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cmath> using namespace std; double s; double dfs(double x,double y,int n) { if(n==1) { return max(x/y,y/x); } double ans=1e10; for(int i=1;i<=n/2;i++) { double tx=x*i/n,ty=y*i/n; ans=min(ans,max(dfs(tx,y,i),dfs(x-tx,y,n-i))); ans=min(ans,max(dfs(x,ty,i),dfs(x,y-ty,n-i))); } return ans; } int x,y,n; int main() { scanf("%d%d%d",&x,&y,&n); s=(x*y)/n; printf("%lf",dfs(x,y,n)); return 0;
bzoj1025
简单推一下就会发现跟博弈论鸡毛关系都没有
题目变成了
“求和为n的数列的最小公倍数种数”
然后随便D一下
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<cstring> #define ll long long using namespace std; int n; int check[1010],pri[1010],cnt; ll dp[1010][1010],res; void laji() { for(int i=2;i<=1000;i++) { if(!check[i])pri[++cnt]=i; for(int j=1;j<=cnt;j++) { if(i*pri[j]>1000)break; check[i*pri[j]]=1; if(i%pri[j]==0)break; } } } int main() { scanf("%d",&n); laji(); dp[0][0]=1; for(int i=1;i<=cnt;i++) { for(int j=0;j<=n;j++)dp[i][j]=dp[i-1][j]; for(int j=pri[i];j<=n;j*=pri[i]) for(int k=0;k<=n-j;k++)dp[i][k+j]+=dp[i-1][k]; } for(int i=0;i<=n;i++)res+=dp[cnt][i]; cout<<res; }