【SDOI2009】解题汇总
来源:互联网 发布:郑州软件测试招聘 编辑:程序博客网 时间:2024/05/21 17:17
又开了波专题,感觉就和炉石开冒险一样...(说的好像我有金币开冒险似的)
/—————————————————————————————————————————————/
BZOJ-1226 【SDOI2009】学校食堂Dining
状态压缩DP
f【i】【j】【k】表示前i-1人都吃过饭,j表示i与i之后7人的吃饭情况,k表示上一个吃饭的人与i的相对位置 转移如程序;
这题需要注意一些小细节: 后面同学的领饭情况需要压8位而不是7位 当一个同学已经领到饭的时候,他的忍耐度就可以忽略了
code:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cstdlib>#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 1010int tim,n,ans;struct data{int t,b;}st[maxn];int f[maxn][1<<9][20];int work(int x,int y){ if (x==0) return 0; return st[x].t^st[y].t; }#define inf 0x7fffffffvoid DP(){ //memset(f,127,sizeof(f)); for(int i=1;i<=n+1;i++) for(int j=0; j<(1<<8);j++) for(int k=-8; k<=7; k++) f[i][j][k+8]=inf; f[1][0][7]=0; for (int i=1; i<=n; i++) for (int j=0; j<(1<<8); j++) for (int k=-8; k<=7; k++) { if (f[i][j][k+8]<inf) if (j&1) f[i+1][j>>1][k+7]=min(f[i][j][k+8],f[i+1][j>>1][k+7]); else { int r=inf; for(int l=0; l<8; l++) { if(!(j&(1<<l))) { if(i+l>r) break; r=min(r,i+l+st[i+l].b); f[i][j+(1<<l)][l+8]=min(f[i][j+(1<<l)][l+8],f[i][j][k+8]+work(i+k,i+l)); } } } }}int main(){ tim=read(); while (tim--) { n=read(); for (int i=1; i<=n; i++) st[i].t=read(),st[i].b=read(); /*puts("OK");*/ DP(); /*puts("OK");*/ ans=inf; for (int i=-8; i<0; i++) ans=min(f[n+1][0][i+8],ans); printf("%d\n",ans); } return 0;}
異次元の传送阵http://blog.csdn.net/dad3zz/article/details/50930294
/—————————————————————————————————————————————/
BZOJ-1227【SDOI2009】虔诚的墓主人
树状数组+离散化+组合数学
如果a,b在同一行,则ans+=c(l[a]+1(包括a),k)*c(r[b]+1,k)再分别乘上ab间的每一个点的c(u[i],k)*c(d[i],k)
l[a],r[a],u[a],d[a]表示一个点上下左右的点数,可以预处理,也可以边做边记录
需要优化时间复杂度,于是要用树状数组维护a到b所有点的c(u[i],k)*c(d[i],k)之和
从左往右处理某行的某一个点时,要将树状数组中该点横坐标位置上的数进行修改
修改的值为就是现在的c(u[i],k)*c[d[i],k]减去原来的,也就是c(u[i],k)*c[d[i],k]-c(u[i]+1,k)*c[d[i]-1,k]
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 maxw 100010#define p 2147483648LLint n,m,w,k,l;struct data{ int x,y; bool operator < (const data & A) const { if (y==A.y) return x<A.x; return y<A.y; }}tr[maxw];long long tree[maxw*2],C[maxw*2][15],ans;int ls[maxw*2],cnt,num,now[maxw*2];int xx[maxw*2],yy[maxw*2];int lowbit(int x){ return x&(-x);}void add(int x,int dat){ for (int i=x; i<=w*2; i+=lowbit(i)) tree[i]=(tree[i]+dat)%p;}long long query(int x){ long long re=0; for (int i=x; i>0; i-=lowbit(i)) re=(re+tree[i])%p; return re;}int getloc(int dat){ int l=1,r=cnt; while (l<=r) { int mid=(l+r)>>1; if (ls[mid]<dat) l=mid+1; else if (ls[mid]>dat) r=mid-1; else return mid; }}void getC(){ C[0][0]=1; for (int i=1; i<=w; i++) { C[i][0]=1; for (int j=1; j<=min(k,i); j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%p; }}int main(){ n=read(),m=read(); w=read(); for (int i=1; i<=w; i++) ls[++cnt]=tr[i].x=read(),ls[++cnt]=tr[i].y=read(); k=read(); sort(ls+1,ls+cnt+1); //for (int i=2; i<=cnt; i++) if (ls[i]!=ls[i-1]) ls[++num]=ls[i]; //for (int i=1; i<=w; i++) tr[i].x=getloc(tr[i].x),tr[i].y=getloc(tr[i].y); for (int i=1; i<=w; i++) xx[getloc(tr[i].x)]++,yy[getloc(tr[i].y)]++; getC(); sort(tr+1,tr+w+1); //for (int i=1; i<=w; i++) //printf("%d %d\n",getloc(tr[i].x),getloc(tr[i].y)); for(int i=1;i<=w;i++) { if(i>1 && tr[i].y==tr[i-1].y) l++,ans+=(query(getloc(tr[i].x)-1)-query(getloc(tr[i-1].x)))*(C[l][k]*C[yy[getloc(tr[i].y)]-l][k]),ans%=p; else l=0; int loc=getloc(tr[i].x); now[loc]++; int delta=(C[now[loc]][k]*C[xx[loc]-now[loc]][k]-C[now[loc]-1][k]*C[xx[loc]-now[loc]+1][k])%p; add(loc,delta); } if (ans<0) ans+=p; printf("%lld\n",ans); return 0;}
異次元の传送阵http://blog.csdn.net/dad3zz/article/details/50930241
/—————————————————————————————————————————————/
BZOJ-1228【SDOI2009】E&D
SG函数+打表找规律
RT….后来发现好像是个叫 ‘’ 阿达马矩阵 ‘’的东西
找规律,然后异或下答案….
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;}int t,n,ans;int sg(int x, int y) { int tmp=1<<30,re=31; for (int i=30; i; i--) { if (x<=tmp && y<=tmp) re=i; else { if (x>tmp) x-=tmp; if (y>tmp) y-=tmp; } tmp>>=1; } if (x==1 && y==1) return 0; return re;}int main(){ t=read(); while (t--) { n=read(),ans=0; int x,y; for (int i=1; i<=n/2; i++) x=read(),y=read(),ans^=sg(x,y); if (ans) puts("YES"); else puts("NO"); } return 0;}
異次元の传送阵http://blog.csdn.net/dad3zz/article/details/50906980
/—————————————————————————————————————————————/
BZOJ-1235【SDOI2009】细胞探索
FloodFill + 大暴力
code:实在是不愿意打搜索QAQ….
異次元の传送阵移至GTY大哥的BLOG吧! http://gaotianyu1350.gitcafe.io/2015/04/07/BZOJ1235-细胞探索/
/—————————————————————————————————————————————/
BZOJ-1875【SDOI2009】HH去散步
DP+矩乘快速幂优化
正常是对点构造矩阵,那么这里用边来构造,保证走的时候不经过反向边即可,然后矩乘快速幂加速
code:
#include<iostream>#include<cstdio>#include<algorithm>#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 25#define maxm 70#define p 45989int n,m,t,st,ed;struct data{int to,next;}edge[maxm*2];int head[maxm],cnt;int l,ans;struct Mat{int a[maxm*2][maxm*2];Mat(){memset(a,0,sizeof(a));}};void add(int u,int v){ cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;}Mat mul(Mat A,Mat B){ Mat re; for (int i=1; i<=l; i++) for (int j=1; j<=l; j++) for (int k=1; k<=l; k++) re.a[i][j]=(re.a[i][j]+(A.a[i][k]*B.a[k][j])%p)%p; return re; }Mat quick_mul(Mat A,int x){ Mat re; for (int i=1; i<=l; i++) re.a[i][i]=1; while (x) { if (x&1) re=mul(re,A); x>>=1; A=mul(A,A); } return re;}int findre(int x){ if (x&1) return x+1; else return x-1;}int main(){ n=read(),m=read(),t=read(),st=read(),ed=read(); for (int i=1; i<=m; i++) { int u=read(),v=read(); add(u,v); add(v,u); } Mat x,y; for (int i=head[st]; i; i=edge[i].next) x.a[1][i]=1; l=cnt; if (t==0) {if (st==ed) puts("1"); else puts("0"); return 0;} for (int i=1; i<=l; i++) for (int j=head[edge[i].to]; j; j=edge[j].next) if (j!=findre(i)) y.a[i][j]=1; x=mul(x,quick_mul(y,t-1)); for (int i=head[ed]; i; i=edge[i].next) ans=(ans+x.a[1][findre(i)])%p; printf("%d\n",ans); return 0;}
異次元の传送阵http://blog.csdn.net/dad3zz/article/details/50930113
/—————————————————————————————————————————————/
BZOJ-1876【SDOI2009】SuperGCD
高精度取模 变态题 +Python
抱歉Python几行干掉
code:
a=(int)(input())b=(int)(input())while b!=0: t=a a=b b=t%bprint(a)
異次元の传送阵http://blog.csdn.net/dad3zz/article/details/50930144
/—————————————————————————————————————————————/
BZOJ-1877【SDOI2009】晨跑
拆点+傻逼费用流
建图: 把除了编号1和n的点拆点,正常连边,若两点相连,用一个点的出点连另一个点的入点;
一个点拆成的入点和出点间连容量为1,费用为0;
源点为1,汇点为n;
最后最大流为最多天数,最小费用为最短路径
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 205#define maxm 20010int n,m;struct data{int to,next,cap,cost;}edge[maxn*2+maxm*2];int head[maxn*2],cnt=1;int S,T;int len[maxn][maxn];void add(int u,int v,int w,int c){ cnt++; edge[cnt].to=v; edge[cnt].cost=c; edge[cnt].cap=w; edge[cnt].next=head[u]; head[u]=cnt;}void insert(int u,int v,int w,int c){ add(u,v,w,c); add(v,u,0,-c);}#define inf 0x7fffffffbool visit[maxn*2];int dis[maxn*2];bool mark[maxn*2];int q[maxm*10],h,t;int anst,ansl;bool spfa(){ memset(visit,0,sizeof(visit)); for (int i=0; i<=(n-2)*2+2; i++) dis[i]=inf; h=0,t=1; q[0]=T;dis[T]=0;visit[T]=1; while (h<t) { int now=q[h];h++; for (int i=head[now]; i; i=edge[i].next) if (edge[i^1].cap && dis[now]-edge[i].cost<dis[edge[i].to]) { dis[edge[i].to]=dis[now]-edge[i].cost; if (!visit[edge[i].to]) { q[t++]=edge[i].to; visit[edge[i].to]=1; } } visit[now]=0; } return dis[S]!=inf;}int dfs(int loc,int low){ mark[loc]=1; if (loc==T) return low; int w,used=0; for (int i=head[loc]; i; i=edge[i].next) if (edge[i].cap && !mark[edge[i].to] && dis[edge[i].to]==dis[loc]-edge[i].cost) { w=dfs(edge[i].to,min(low-used,edge[i].cap)); ansl+=w*edge[i].cost; used+=w; edge[i].cap-=w;edge[i^1].cap+=w; if (used==low) return low; } return used;}void zkw(){ int tmp=0; while (spfa()) { mark[T]=1; while (mark[T]) { memset(mark,0,sizeof(mark)); tmp+=dfs(S,inf); } } anst=tmp;}//一眼拆点,给定的长度为费用,每条边的容量为1 void make(){ S=1; T=n; for (int i=2; i<=n-1; i++) insert(i,i+n-1,1,0); for (int i=2; i<=n-1; i++) for (int j=1; j<=n; j++) if (len[i][j]!=0) insert(i+n-1,j,1,len[i][j]); for (int i=1; i<=n; i++) if (len[1][i]!=0) insert(1,i,1,len[1][i]);}int main(){ n=read(),m=read(); int from,to; for (int i=1; i<=m; i++) from=read(),to=read(),len[from][to]=read(); make(); zkw(); printf("%d %d\n",anst,ansl); return 0;}
異次元の传送阵http://blog.csdn.net/dad3zz/article/details/50907874
/—————————————————————————————————————————————/
BZOJ-1878【SDOI2009】HH的项链
树状数组+莫队算法
在线操作的话,无法判重,所以考虑离线。 对询问的左端排序,然后从小到大做;严格意义上不是莫队,但是是应用了莫队的思想。
然后在读入颜色时,进行些处理,处理出每个颜色下一次出现的位置。 在处理询问时,加入下一个颜色,答案即为前缀和相减。
code:
#include<iostream>#include<cstdio>#include<algorithm>#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 50010#define maxm 200010#define maxcol 1000010int n,m,maxcolor;int color[maxn];struct data{ int l,r,id; bool operator < (const data & A) const { return l<A.l; }}ask[maxm];int ans[maxm];int next[maxn],pre[maxcol];int tree[maxn];int lowbit(int x){ return x&(-x);}void add(int x,int dat){ for (int i=x; i<=n; i+=lowbit(i)) tree[i]+=dat;}int query(int x){ int re=0; for (int i=x; i>0; i-=lowbit(i)) re+=tree[i]; return re;}int main(){ n=read(); for (int i=1; i<=n; i++) color[i]=read(),maxcolor=max(maxcolor,color[i]); for (int i=n; i>0; i--) next[i]=pre[color[i]],pre[color[i]]=i; for (int i=1; i<=maxcolor; i++) if (pre[i]) add(pre[i],1); m=read(); for (int i=1; i<=m; i++) ask[i].l=read(),ask[i].r=read(),ask[i].id=i; sort(ask+1,ask+m+1); int loc=1; for (int i=1; i<=m; i++) { while (loc<ask[i].l) { if (next[loc]) add(next[loc],1); loc++; } ans[ask[i].id]=query(ask[i].r)-query(ask[i].l-1); //printf("%d %d %d %d\n",ask[i].l,ask[i].r,query(ask[i].r),query(ask[i].l-1)); } for (int i=1; i<=m; i++) printf("%d\n",ans[i]); return 0;}
異次元の传送阵http://blog.csdn.net/dad3zz/article/details/50908757
/—————————————————————————————————————————————/
BZOJ-1879【SDOI2009】Bill的挑战
状态压缩DP
思路比较简单:
f【i】【j】表示 匹配到第i位,时状态为j的方案数;
具体的转移:
f[i+1][j&(g[i][l])]+=f[i][j],f[i+1][j&(g[i][l])]%=p;
用g来存储状态,枚举状态即可;
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 p 1000003int t,n,k;char s[20][60];int f[60][1<<15],g[60][1<<5];void DP(){ memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); int len=strlen(s[1]); for (int i=0; i<len; i++) for (int j=1; j<=n; j++) for (int l=0; l<26; l++) if (s[j][i]=='?' || s[j][i]=='a'+l) g[i][l]|=1<<(j-1); f[0][(1<<n)-1]=1; for (int i=0; i<len; i++) for (int j=0; j<(1<<n); j++) if (f[i][j]!=0) for (int l=0; l<26; l++) f[i+1][j&(g[i][l])]+=f[i][j],f[i+1][j&(g[i][l])]%=p; int ans=0; for (int i=0; i<(1<<n); i++) { int now=i,tmp=0; while (now) tmp+=now&1,now>>=1; if (tmp==k) ans=(ans+f[len][i])%p; } printf("%d\n",ans);}int main(){ t=read(); while (t--) { n=read(),k=read(); for (int i=1; i<=n; i++) scanf("%s",s[i]); DP(); } return 0;}
異次元の传送阵http://blog.csdn.net/dad3zz/article/details/50937084
/—————————————————————————————————————————————/
BZOJ-1880【SDOI2009】Elaxia的路线
SPFA+枚举
4遍spfa,开四个dis数组,分别记录st1,st2,ed1,ed2到各点的最短路,然后枚举点对(i,j)判断i,j是否在最短路径上,然后更新答案即可.
code:
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#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 maxn 1510#define maxm 500010int n,m,ans;int st1,st2,ed1,ed2;int len1,len2;struct data{int to,next,tim;}edge[maxm*2];int head[maxn],cnt;void add(int u,int v,int t){ cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].tim=t;}void insert(int u,int v,int t){ add(u,v,t); add(v,u,t);}int disst1[maxn],disst2[maxn],dised1[maxn],dised2[maxn];bool visit[maxn];#define inf 0x7fffffffvoid spfa(int s,int* dis){ queue<int>q; for (int i=1; i<=n; i++) dis[i]=inf; q.push(s); dis[s]=0; while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i; i=edge[i].next) if (dis[now]+edge[i].tim<dis[edge[i].to]) { dis[edge[i].to]=edge[i].tim+dis[now]; if (!visit[edge[i].to]) { q.push(edge[i].to); visit[edge[i].to]=1; } } visit[now]=0; }}bool check(int loc){ if (disst1[loc]+dised1[loc]!=len1 || disst2[loc]+dised2[loc]!=len2) return false; return true;}int main(){ n=read(),m=read(); st1=read(),ed1=read(),st2=read(),ed2=read(); for (int i=1; i<=m; i++) { int u=read(),v=read(),t=read(); insert(u,v,t); } spfa(st1,disst1); spfa(st2,disst2); spfa(ed1,dised1); spfa(ed2,dised2); len1=disst1[ed1]; len2=disst2[ed2]; for (int i=1; i<=n; i++) if (check(i)) for (int j=1; j<=n; j++) if (check(j)) ans=max(ans,abs(disst1[i]-disst1[j])); printf("%d\n",ans); return 0;}
異次元の传送阵http://blog.csdn.net/dad3zz/article/details/50930197
/—————————————————————————————————————————————/
总结:
SDOI2009没什么大数据结构题,但是DP相当多吗,状压DP更是出现两道….当务之急是抓好DP,类似的思想要记住。 对于状压DP,在写转移之前,先思考需要如何转移,再思考状压的状态即可,一般用and,or等进行变换,不能慌。
对于一些数论题,或者博弈题,找规律不能空想,先暴力打标,对表找规律。
数据值极大,但数据量不大,且需要用值时,首先考虑离散即可,很多时候,不要求强制在线,可以优先考虑下离线的做法,可能效果拔群
网络流建模不要慌,对于限制次数,只需要拆点。(这不是早就知道的吗…)
成果图:
- 【SDOI2009】解题汇总
- [SDOI2009]HH的项链 解题报告
- SDOI2009
- LeetCode解题报告汇总
- 【SDOI2008】解题汇总
- WOJ 解题报告汇总
- 解题方法技巧汇总类
- 二叉树中序遍历解题汇总
- PAT甲级解题报告汇总
- [SDOI2009]晨跑
- SDOI2009 晨跑
- SDOI2009晨跑
- 【SDOI2009】学校食堂
- “[SDOI2009] 晨跑”
- [SDOI2009]学校食堂
- [SDOI2009]SuperGCD
- [汇总]字符串题目推荐及解题报告
- 汇总]搜索题目推荐及解题报告
- STL适配器(ostream_itertor与istream_iterator)
- 使用GridView以表格形式显示多张图片
- 编写可读代码的艺术 (4)
- Python学习笔记:函数参数
- 5-22 龟兔赛跑
- 【SDOI2009】解题汇总
- lua bytecode 编译
- 微信开发
- 练习_窗口
- alter table *** add constraint *** 用法
- 懒人cocos2dx系列之一 资源环境配置
- 基础补完系列 - C++ Primer Plus 第二章
- 在家乡做什么生意比较好?且看雷家第一智囊的分析
- 二进制,8进制,16进制,10进制