NOI 2008 Day2 ~ 2009
[NOI2008]糖果雨
啊啊啊先挖坑,推荐
http://blog.sina.com.cn/s/blog_86942b1401015yln.html
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=2003; const int maxm=4003; const int maxc=1e6+10; int q,len,n,m; int s[2][maxn][maxm],x[maxc],y[maxc]; inline int lowbit(int x){return x&-x;} inline void add(int p,int x,int y,int v) { x++,y++; for(;x<maxn;x+=lowbit(x)) for(int i=y;i<maxm;i+=lowbit(i)) s[p][x][i]+=v; } inline int sum(int p,int x,int y) { if(x<0 || y<0) return 0; int k=0; if(++x>n) x=n+1; if(++y>m) y=m+1; for(;x;x-=lowbit(x)) for(int i=y;i;i-=lowbit(i)) k+=s[p][x][i]; return k; } inline void update(int t,int c,int l,int r,int d) { x[c]=(t-l*d+n)%n,y[c]=r-l; add(0,x[c],y[c]+x[c],1),add(1,x[c],y[c]-x[c]+n,1); } inline void del(int c) { add(0,x[c],y[c]+x[c],-1),add(1,x[c],y[c]-x[c]+n,-1); } inline int area(int p,int x,int y,int xx,int yy) { return sum(p,xx,yy)+sum(p,x-1,y-1)-sum(p,x-1,yy)-sum(p,xx,y-1); } inline int ask(int t,int l,int r) { int d=r==len; return area(0,t,t+l,t+r,m)+area(0,0,l+t-n,t+r-n-d,m)+area(1,n-r+t+d,l-t,n,m)+area(1,t-r,l-t+n,t-1,m); } int main() { scanf("%d%d",&q,&len); n=len<<1,m=len<<2; while(q--) { int t,c,l,r,d,p; scanf("%d%d",&p,&t); if(p==1) { scanf("%d%d%d%d",&c,&l,&r,&d); update(t,c,l,r,d); } else if(p==2) { scanf("%d%d",&l,&r); printf("%d\n",ask(t%n,l,r)); } else { scanf("%d",&c); del(c); } } return 0; }
[NOI2009]诗人小G
注意字母和空格也算‘字符’。
f[i]表示考虑到i这一句诗的最少花费。
我们显然有经典的1D1D模型:f[i]=min(f[j]+cost[j+1][i]);
然而n^2只有30(或者20)
想办法啊,观察cost,'发现'满足四边形不等式(可以直接打表观察决策单调的规律),那么f[i]就具有决策单调性,每个f[j]可以更新的f[i]是一个区间,而且所有的f[j]的区间不重不漏,发现我们总能找到一个i,使得i之前的f[i]不需要f[j]达到最优,而之后的总可以借助f[j]达到最优,那么用一个队列存所有的线段以及用谁更新,每次先弹掉右端小于当前i的区间(其实每次只弹一个),然后用队首更新f[i],入队的时候尝试通过二分把队尾的区间右端往回压(当队尾的左端点都是i更新更优的时候,直接弹掉q[top]),即找到i和q[top]的更新分界点,入队就好了。nlogn的吧。
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=1e5+10; struct point { int l,r,p; }q[maxn*20]; int n,T; long double L,P,sum[maxn],f[maxn]; int ans[maxn],nans,from[maxn]; char ss[maxn][55]; inline long double Pow(long double x,long long b) { if(x<0)x=-x; long double res=1; for(;b;b>>=1) { if(b&1) res=res*x; x=x*x; } return res; } inline long double cal(int j,int i) { return f[j]+Pow(sum[i]-sum[j]+i-j-1-L,P); } int find(point t,int x) { int l=t.l-1,r=t.r+1; while(l+1<r) { int mid=(l+r)>>1; if(cal(t.p,mid)<cal(x,mid)) l=mid; else r=mid; } return r; } inline void dp() { int head=1,tail=0; q[++tail]=(point){0,n,0}; for(int i=1;i<=n;i++) { if(head<=tail && q[head].r<i) head++; f[i]=cal(q[head].p,i); from[i]=q[head].p; if(head>tail || cal(i,n)<=cal(q[tail].p,n)) { while(head<=tail && cal(i,q[tail].l)<=cal(q[tail].p,q[tail].l)) tail--; if(head>tail) q[++tail]=(point){i,n,i}; else { int pos=find(q[tail],i); q[tail].r=pos-1; q[++tail]=(point){pos,n,i}; } } } } inline void print() { int tmp=n; nans=0; while(1) { ans[++nans]=tmp; if(tmp==0) break; tmp=from[tmp]; } for(int i=nans;i>1;i--) { int be=ans[i]+1; for(int j=be;j<=ans[i-1];j++) printf("%s%c",ss[j]+1,j==ans[i-1]?'\n':' '); } } int main() { scanf("%d",&T); while(T--) { memset(f,0,sizeof(f)); scanf("%d%Lf%Lf",&n,&L,&P); for(int i=1;i<=n;i++) { scanf("%s",ss[i]+1); sum[i]=sum[i-1]+strlen(ss[i]+1); } dp(); if(f[n]>1000000000000000000LL) { printf("Too hard to arrange\n"); printf("--------------------"); if(T) printf("\n"); continue; } printf("%.0Lf\n",f[n]); print(); printf("--------------------"); if(T) printf("\n"); } return 0; }
[NOI2009]植物大战僵尸
啊。。很棘手的一道题。
一眼最大权闭合子图,可是有毛病的啊,图中会有环。
想想就会发现,对于环来说,我们肯定无论如何也得不到环中的分,所以环中的点所保护的点当然也无法得分,所以要通过拓扑来忽略这些点,删掉环和从环伸出去的点?反向拓扑。
然后就很傻了,模板。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int maxn=610*610*6+10; const int inf=1e9; struct point { int to; int nxt; int c; }edge1[maxn],edge[maxn]; int n,m,cnt=0,s,t,sum,tot1,tot; int level[maxn],vis[maxn],val[maxn]; int head1[maxn],head[maxn],in[maxn]; int map[50][50]; inline void add1(int u,int v) { tot1++; edge1[tot1].to=v; edge1[tot1].nxt=head1[u]; head1[u]=tot1; } inline void add(int u,int v,int c) { edge[tot].to=v; edge[tot].nxt=head[u]; edge[tot].c=c; head[u]=tot; tot++; edge[tot].to=u; edge[tot].nxt=head[v]; edge[tot].c=0; head[v]=tot; tot++; } inline void Topo() { queue<int> q; for(int i=1;i<=n*m;++i) if(in[i]==0) { q.push(i); vis[i]=1; } while(!q.empty()) { int tt=q.front(); q.pop(); for(int i=head1[tt];i;i=edge1[i].nxt) { int v=edge1[i].to; in[v]--; if(in[v]==0) q.push(v); } } } inline bool Bfs() { queue<int> q; memset(level,-1,sizeof(level)); level[s]=1; q.push(s); while(!q.empty()) { int tt=q.front(); q.pop(); for(int i=head[tt];~i;i=edge[i].nxt) { int v=edge[i].to; if(level[v]==-1 && edge[i].c) { level[v]=level[tt]+1; q.push(v); } } } if(level[t]==-1) return false; return true; } int dfs(int x,int low) { if(x==t || low==0) return low; int res=0; for(int i=head[x];~i;i=edge[i].nxt) { int v=edge[i].to; if(level[v]==level[x]+1 && edge[i].c) { int ww=dfs(v,min(low,edge[i].c)); low-=ww; res+=ww; edge[i].c-=ww; edge[i^1].c+=ww; if(low==0) break; } } if(res!=low || res==0) level[x]=-1; return res; } int Dinic() { int res=0; while(Bfs()) { res+=dfs(s,inf); } return res; } int main() { scanf("%d%d",&n,&m); s=0,t=m*n+1; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) map[i][j]=++cnt; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { int vv,nn; scanf("%d%d",&vv,&nn); val[map[i][j]]=vv; for(int t=1;t<=nn;t++) { int xx,yy; scanf("%d%d",&xx,&yy); xx++,yy++; add1(map[i][j],map[xx][yy]); in[map[xx][yy]]++; } if(j!=m) { add1(map[i][j+1],map[i][j]); in[map[i][j]]++; } } Topo(); memset(head,-1,sizeof(head)); for(int x=1;x<=m*n;++x) if(!in[x]) { if(val[x]>0) { add(s,x,val[x]); sum+=val[x]; } if(val[x]<0) add(x,t,-val[x]); for(int i=head1[x];i;i=edge1[i].nxt) { int v=edge1[i].to; if(!in[x]) add(v,x,inf); } } int Mincut=Dinic(); printf("%d",sum-Mincut); return 0; }
[NOI2009]管道取珠
很可爱的一道题/
只要发现根据乘法原理,所谓Ai^2就是再搬一套双筒管放在下面,使得上下系统同时达到同种状态的操作方案数,那就很naive了。
用f[i][s1][s2]表示操作i步,上面的双筒的上管已经弹出s1个球,下面的双筒的上管已经弹出s2个球的方案数。第一维可以压掉。
O((n+m)n^2)的,卡着过。
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=505; const int mod=1024523; int n,m; long long f[2][maxn][maxn]; char a[maxn],b[maxn],c[maxn]; int main() { scanf("%d%d",&n,&m); scanf("%s",a+1); scanf("%s",b+1); f[0][0][0]=1; int cur=1; for(int t=1;t<=n+m;t++) { memset(f[cur],0,sizeof(f[cur])); for(int i=max(0,t-m);i<=min(n,t);i++) for(int j=max(0,t-m);j<=min(n,t);j++) { if(t-i && t-j && b[t-i]==b[t-j]) f[cur][i][j]=(f[cur][i][j]+f[cur^1][i][j])%mod; if(i && j && a[i]==a[j]) f[cur][i][j]=(f[cur][i][j]+f[cur^1][i-1][j-1])%mod; if(t-j && i && a[i]==b[t-j]) f[cur][i][j]=(f[cur][i][j]+f[cur^1][i-1][j])%mod; if(t-i && j && b[t-i]==a[j]) f[cur][i][j]=(f[cur][i][j]+f[cur^1][i][j-1])%mod; } cur^=1; } printf("%d",f[cur^1][n][n]); return 0; }