[POJ3762 The Bonus Salary!]费用流

【题目大意】
给N个时间段,每个时间段只能用一次,然后有k天供你用这些时间段。
同一天里,时间段不能有重叠。
问最多能用到多少费用?
【算法分析】
先对所有时刻进行离散化。
然后每个时刻向下一个时刻连容量为k,费用为0的边。
接着对于每个区间 [L,R],对对应的时间连边L->R费用为区间费用,容量为1.
然后源点要退一个格子。
最后搞一个最大费用最大流就可以了。
【其它】
1CE,悲剧,POJ的iostream里不含sort函数。。。
【CODE】
#include #include #include #include #include using namespace std;
const int N=20000;
struct edge{int x,y,c,w;edge *op,*next;}g[N],*ls[N],*fa[N];
struct Node{int x,y,w;}p[N];
int n,k,tot,e,cost;
int lx[N],d[N],v[N],List[N];

void init(){
tot=0; e=0; memset(ls,0,sizeof(ls));
for (int i=1;i<=n;i++){
int h1,m1,s1,h2,m2,s2;
scanf("%d:%d:%d %d:%d:%d %d",&h1,&m1,&s1,&h2,&m2,&s2,&p[i].w);
p[i].x=h1*3600+m1*60+s1;
p[i].y=h2*3600+m2*60+s2;
lx[++tot]=p[i].x;
lx[++tot]=p[i].y;
}
}

void addedge(int x,int y,int c,int w){
g[++e].x=x; g[e].y=y; g[e].c=c; g[e].w=w; g[e].next=ls[x];
ls[x]=&g[e]; g[e].op=&g[e+1];
g[++e].x=y; g[e].y=x; g[e].c=0; g[e].w=-w; g[e].next=ls[y];
ls[y]=&g[e]; g[e].op=&g[e-1];
}

int Find(int x){
int l=1,r=tot,mid;
while (l mid=(l+r)/2;
if (x>lx[mid]) l=mid+1;
else r=mid;
}
return l;
}

void Lisan(){
sort(lx+1,lx+tot+1);
int tmp=tot,i; tot=0;
for (i=1;i<=tmp;i++)
if (!tot || lx[tot]!=lx[i]) lx[++tot]=lx[i];
for (i=1;i<=n;i++)
addedge(Find(p[i].x),Find(p[i].y),1,p[i].w);
for (i=1;i addedge(i,i+1,k,0);
addedge(0,1,k,0);
}

bool spfa(){
memset(d,200,sizeof(d));
memset(v,0,sizeof(v));
int head=0,tail=1;
List[1]=0; v[0]=1; d[0]=0;
while (head!=tail){
head++; if (head>=N) head=0;
for (edge *t=ls[List[head]];t;t=t->next)
if (t->c && d[t->x]+t->w>d[t->y]){
d[t->y]=d[t->x]+t->w;
fa[t->y]=t;
if (!v[t->y]){
v[t->y]=1;
tail++; if (tail>=N) tail=0;
List[tail]=t->y;
}
}
v[List[head]]=0;
}
if (d[tot]<=0) return false;
return true;
}

void change(){
int nf=0x7FFFFFFF;
for (int i=tot;i;i=fa[i]->x)
if (fa[i]->c cost+=nf*d[tot];
for (int i=tot;i;i=fa[i]->x){
fa[i]->c-=nf;
fa[i]->op->c+=nf;
}
}

void MCMF(){
cost=0;
while (spfa()) change();
}

int main(){
while (scanf("%d%d",&n,&k)!=EOF){
init();
Lisan();
MCMF();
printf("%dn",cost);
}
return 0;
}

[Elite 2010 February Competition Gold 【corral】]二进制步长分解、动态内存水过~★★

【题目链接】http://ace.delos.com/ioigate
八中也有。。。但是八中现在暂时爆了。等它好了的时候,搜索USACO2010就能很容易找到。
【算法分析】
原创傻×做法。。。
首先我们容易发现,假设对于两个围栏,如果一个包括另外一个的话,那么被包的就废了。
这样搞完以后呢,我们发现一个重要性质,假设用[l,r]来表示一个围栏,那么如果按l排序的话,那么r也是递增的!
我们先把数组复制多一次。这样就可以避免圈的情况。
题目转化成,在这个长度为2*c的长栏里,找一个长度为c的部分被完全覆盖。使之用的围栏最少。
我们很容易想到每次找下一个满足Lk<=Ri,且Lk最大的围栏来搞。
直到覆盖长度超过>=c。
那么我们设Right[i]表示对于当前这个栅栏i的下一个最优栅栏k。
然后我们解决问题时就可以枚举开头,然后一直Right下去统计就可以了。
但是会超时。
于是我们把Next[k][i]定义为i这个围栏,Right了2^k次以后到达哪一个围栏。
然后我们走的时候只要跟着这个跳着走就可以了。
于是问题可以在限定时间内水过。
【时间复杂度】O(n lg n)
【空间复杂度】O(n lg n)
【其它】
重要的是。。。USACO的内存少得可怜,我也不知道它到底有多少,反正就是会爆。。。
于是我百般无奈之下:malloc申请刚好大小的数组。
终于AC~
另外我依然不怕死地对10^5的读入用了cin。。
【时间与空间情况】
> Run 1: OK [0.011 secs, 4792 KB]
> Run 2: OK [0.022 secs, 4792 KB]
> Run 3: OK [0.011 secs, 4792 KB]
> Run 4: OK [0.011 secs, 4924 KB]
> Run 5: OK [0.000 secs, 4924 KB]
> Run 6: OK [0.011 secs, 5080 KB]
> Run 7: OK [0.032 secs, 5412 KB]
> Run 8: OK [0.130 secs, 7920 KB]
> Run 9: OK [0.248 secs, 11344 KB]
> Run 10: OK [0.248 secs, 11272 KB]
> Run 11: OK [0.259 secs, 16116 KB]
> Run 12: OK [0.238 secs, 11056 KB]
> Run 13: OK [0.248 secs, 8872 KB]
【CODE】
/*
ID:jie22221
TASK:corral
LANG:C++
*/
#include #include #include #include #include using namespace std;
const int N=105555*2;
struct Node{int p,l;}q[N];
bool del[N];
int n,c,mp;
int *Right,*next[20];

void input(){
cin >> c >> n;
for (int i=1;i<=n;i++)
cin >> q[i].p >> q[i].l;
}

bool cmp(Node A,Node B){
if (A.p!=B.p) return A.p return A.l>B.l;
}

void init(){
memset(del,false,sizeof(del));
for (int i=1;i<=n;i++){
q[i+n]=q[i];
q[i+n].p+=c;
}
for (int i=1,Max=-1;i<=2*n;i++)
if (q[i].p+q[i].l<=Max) del[i]=true;
else Max=q[i].p+q[i].l;
int tot=0;
for (int i=1;i<=2*n;i++)
if (!del[i]) q[++tot]=q[i];
n=tot;
}

void makenext(){
int i,j=1,k;
for (k=1;1< mp=k;
Right=(int*)malloc(sizeof(int)*(n+1));
for (i=0;i next[i]=(int*)malloc(sizeof(int)*(n+1));
for (i=1;i<=n;i++){
while (j Right[i]=j;
}
for (i=1;i<=n;i++)
next[0][i]=Right[i];
for (k=1;1< for (i=1;i<=n;i++)
next[k][i]=next[k-1][next[k-1][i]];
}
mp=k;
}

void work(){
int ans=0x7FFFFFFF,i,j,k,tmp,done;
for (k=1;q[k].p tmp=q[k].p+c;
done=1;
for (i=k,j=mp-1;j>=0;j–){
int &t=next[j][i];
if (q[t].p+q[t].l done+=1< i=t;
}
}
done++;
ans=min(ans,done);
}
cout << ans << endl;
}

int main(){
freopen("corral.in","r",stdin);
freopen("corral.out","w",stdout);
ios::sync_with_stdio(false);
input();
sort(q+1,q+n+1,cmp);
init();
makenext();
work();
return 0;
}

[Elite 2010 January Competition Gold 【hayturn】]博弈类动态规划★★

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1783
【吐槽】
唉唉唉唉。。。
看了解题报告才会。太水了= =。。。
一开始看错题,写了个dp,样例都不过,后来看了解题报告才理解了题意。。。

【算法分析】
下面基本是对解题报告的翻译:
令Fa[i]表示当前到这只牛选,选的范围是[i,n],然后这只牛到最后最多能得到多少分?
令Fb[i]表示当前到另外一只牛选,选的范围是[i,n],然后到最后这只牛最多能得到多少分?
然后我们应当注意到,主动权在选的那只牛手里。
现在我们有两种决策。
1、选第i格这个权。
那么Fa[i]=Fb[i+1]+w[i];
同时Fb[i]=Fa[i+1];
2、不选第i格这个权。
那么Fa[i]=Fa[i+1];
同时Fb[i]=Fb[i+1];

注意到主动权在当前选那只牛的手里,那么我们以当前选这只牛为上帝进行决策。
就是要取最大的Fa[i]。
所以只要比较Fb[i+1]+w[i]和Fa[i+1]就可以了。。。
【其它】cin取消同步以后原来不是很慢!!!就是800+MS。我用scanf也就500+MS…
当然,要G++才可以。就像外挂一样。
【CODE】
#include using namespace std;
typedef long long lld;
const lld N=705555;
lld n,w[N],Fa[N],Fb[N];

void solve(){
Fa[n]=w[n];
Fb[n]=0;
int i;
for (i=n-1;i>=1;i–)
if (Fb[i+1]+w[i]>=Fa[i+1]){
Fa[i]=Fb[i+1]+w[i];
Fb[i]=Fa[i+1];
}
else{
Fa[i]=Fa[i+1];
Fb[i]=Fb[i+1];
}
cout << Fa[1] << " " << Fb[1] << endl;
}

int main(){
ios::sync_with_stdio(false);
cin >> n;
for (lld i=1;i<=n;i++) cin >> w[i];
solve();
return 0;
}

[Elite 2010 January Competition Gold 【telephone】]树形dp

【题目链接】http://ace.delos.com/ioigate
另外八中OJ的1785也是。不过八中的可能pascal会爆栈。。。
【算法分析】
首先建议看英文,八中的翻译有点歧义。
这个一颗无根树,于是我们可以选一个非叶结点出来当根,那么就可以搞了。
然后我们注意到,如果两个叶子搞在一块的话,那么必然是连到它们的LCA那里去搞了。
如果是这样的话,如果是从某一个结点搞上它父亲那里的话,无论是哪个都是一样的。
因为都是占用父亲的链。
而且显然是越早解决越好。
于是我们直接用一个rest数组记录剩下多少个需要用连向父亲这条边来解决。
然后,都算不上dp了,就是统计算一下就可以了。
【其它】1Y
【CODE】
/*
ID:jie22221
TASK:telephone
LANG:C++
*/
#include #include #include #include using namespace std;
const int N=105555;
struct edge{int x,y,next;}g[N*2];
int n,k,e,root,ans;
int ls[N],du[N],rest[N];

void add(int x,int y){
e++;
g[e].x=x; g[e].y=y; g[e].next=ls[x]; ls[x]=e;
}

void init(){
memset(ls,0,sizeof(ls));
memset(du,0,sizeof(du));
e=0;
scanf("%d%d",&n,&k);
for (int x,y,i=1;i scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
du[x]++; du[y]++;
}
}

void dfs(int p,int fa){
if (du[p]==1){
rest[p]=1;
return;
}
for (int t=ls[p];t;t=g[t].next)
if (g[t].y!=fa){
dfs(g[t].y,p);
rest[p]+=rest[g[t].y];
}
if (rest[p]<=2*k && rest[p]%2){
ans+=rest[p]>>1;
rest[p]=1;
}
else{
if (rest[p]>2*k) ans+=k;
else ans+=rest[p]>>1;
rest[p]=0;
}
}

void solve(){
if (n<=2){
printf("%dn",n);
return;
}
for (int i=1;i<=n;i++)
if (du[i]>1){
root=i;
break;
}
memset(rest,0,sizeof(rest));
ans=0;
dfs(root,0);
}

int main(){
init();
solve();
cout << ans << endl;
return 0;
}

[JSOI2010 缓存交换]贪心、二叉堆

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1826
【算法分析】
如果一个x已经在缓存中,那么本次x进入就不需要再被计算。
也就是说,无论弄谁,本质上都是一样的。
而且当前这个点,必须被push进缓存。
容易得到一个贪心解法:
设当前值为x,就是按下一个x的位置前后排个序里,越后的越废。
于是只取前m个就可以了。于是可以用个二叉堆优化。
【时间复杂度】O(n lg n)
【其它】2Y。一开始把堆得del函数写错了呃@.@,因为删的不一定是第一个,写不习惯了。。
【CODE】
#include using namespace std;
const int N=105555;
struct edge{int y,next,pos;}g[N];
int n,m,e;
int a[N],lx[N],ls[N],Next[N],zz[N];

struct Heap_t{
int tot;
struct Node{int key,pos;}h[N];
void Swap(int i,int j){
Node tmp=h[i]; h[i]=h[j]; h[j]=tmp;
zz[h[i].pos]=i;
zz[h[j].pos]=j;
}
void up(int k){
while (k>1 && h[k].key>h[k/2].key){
Swap(k,k/2);
k/=2;
}
}

void down(int p){
int k;
while (p*2<=tot){
k=p*2;
if (k if (h[k].key>h[p].key){
Swap(k,p);
p=k;
}else break;
}
}

void ins(int key,int pos){
tot++;
h[tot].key=key;
h[tot].pos=pos;
zz[pos]=tot;
up(tot);
}

void del(int p){
Swap(p,tot);
zz[h[tot].pos]=0;
tot–;
up(p);
down(p);
}
}heap;

int Find(int x){
int l=1,r=n,mid;
while (l mid=(l+r)/2;
if (x>lx[mid]) l=mid+1;
else r=mid;
}
return l;
}

void init(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
lx[i]=a[i];
}
sort(lx+1,lx+n+1);
for (int i=1;i<=n;i++) a[i]=Find(a[i]);
for (int i=1;i<=n;i++) ls[i]=n+1;
for (int i=n;i>=1;i–){
Next[i]=ls[a[i]];
ls[a[i]]=i;
}
}

void work(){
memset(zz,0,sizeof(zz));
heap.tot=0;
int i,ans=0;
for (i=1;i<=n;i++){
if (zz[a[i]]==0){
ans++;
if (heap.tot==m) heap.del(1);
heap.ins(Next[i],a[i]);
}
else{
heap.del(zz[a[i]]);
heap.ins(Next[i],a[i]);
}
}
printf("%dn",ans);
}

int main(){
init();
work();
}