水题大作战

来源:互联网 发布:linux 卸载svn 编辑:程序博客网 时间:2024/05/01 22:37

为了表面上我做了很多题我决定刷水题
1597: [Usaco2008 Mar]土地购买

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 5498 Solved: 2055
[Submit][Status][Discuss]
Description

农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <
= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价
格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要
付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.
Input

  • 第1行: 一个数: N
  • 第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽
    Output

  • 第一行: 最小的可行费用.

Sample Input

4

100 1

15 15

20 5

1 100

输入解释:

共有4块土地.
Sample Output

500

FJ分3组买这些土地:

第一组:100x1,

第二组1x100,

第三组20x5 和 15x15 plot.

每组的价格分别为100,100,300, 总共500.
HINT

Source

Gold

第一题bzoj上usaco的题里做的最多的一道题
这种题一定是水题喽
我们想想就知道如果一个小的可以被大的覆盖那和没有是一样的
所以我们先安长度为第一关键字宽度为第二关键字
那么最后剩下的一堆矩形因为我们排序了所以长度一定是递减的,宽度一定递增如果不递增那么一定会被覆盖。
剩下的那一堆矩形这堆东西一定有决策单调性
从网上的题解中不难发现最基础的转移方程就是
f[i]=min(f[j]+kuan[j+1]*chang[i])

那么如果j比k更优说明
f[j]+kuan[j+1]✖️chang[i] < f[k]+kuan[k+1]✖️chang[i]

上过小学的话你就能得到
(f[k]-f[j])/(kuan[j+1]-kuan[k+1])>chang[i]
那么左边这坨东西肯定是越大越好
那么我们就用单调队列维护这坨东西
为了防止出现卡精度的问题,我们要在比较的时候有移项的好习惯
代码很短

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;struct node{    long long x,y;}s[50005];int n,m,cnt;long long f[50005];int q[50005];int maxx,maxy;int head,tail;bool cmp(node x,node y){    if(x.x==y.x) return x.y>y.y;    return x.x>y.x;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        scanf("%lld%lld",&s[i].x,&s[i].y);    }    sort(s+1,s+n+1,cmp);//  maxx=s[1].x,maxy=s[1].y;//  cnt=1;    for(int i=1;i<=n;i++)    {        if(s[i].y>s[cnt].y){            //maxy=s[i].y;            s[++cnt]=s[i];        }    }    //f[1]=s[1].x*s[1].y;    head++,tail++;q[1]=0;    for(int i=1;i<=cnt;i++)    {        while(head<tail&&s[i].y*(s[q[head]+1].x-s[q[head+1]+1].x)>=f[q[head+1]]-f[q[head]]) head++;        f[i]=f[q[head]]+s[q[head]+1].x*s[i].y;        while(head<tail&&(f[q[tail]]-f[q[tail-1]])*(s[q[tail]+1].x-s[i+1].x)>(f[i]-f[q[tail]])*(s[q[tail-1]+1].x-s[q[tail]+1].x)) tail--;        q[++tail]=i;    }    printf("%lld\n",f[cnt]);    return 0;}

1610: [Usaco2008 Feb]Line连线游戏

Time Limit: 5 Sec Memory Limit: 64 MB
Submit: 2399 Solved: 1083
[Submit][Status][Discuss]
Description

Farmer John最近发明了一个游戏,来考验自命不凡的贝茜。游戏开始的时 候,FJ会给贝茜一块画着N (2 <= N <= 200)个不重合的点的木板,其中第i个点 的横、纵坐标分别为X_i和Y_i (-1,000 <= X_i <=1,000; -1,000 <= Y_i <= 1,000)。 贝茜可以选两个点画一条过它们的直线,当且仅当平面上不存在与画出直线 平行的直线。游戏结束时贝茜的得分,就是她画出的直线的总条数。为了在游戏 中胜出,贝茜找到了你,希望你帮她计算一下最大可能得分。

Input

  • 第1行: 输入1个正整数:N

    • 第2..N+1行: 第i+1行用2个用空格隔开的整数X_i、Y_i,描述了点i的坐标

Output

第1行: 输出1个整数,表示贝茜的最大得分,即她能画出的互不平行的直线数

Sample Input

4

-1 1

-2 0

0 0

1 1

Sample Output

  • 第1行: 输出1个整数,表示贝茜的最大得分,即她能画出的互不平行的直线数

HINT

4 输出说明: 贝茜能画出以下4种斜率的直线:-1,0,1/3以及1。

Source

Silver

看到以后想了想
这种题肯定要卡精度吧,要不太傻逼了
但是他就是超级傻逼题
直接枚举所有的连线得到所有的斜率排序去重

#include<bits/stdc++.h>using namespace std;struct node{    double x,y;}t[205];double q[40005];int n;int tot;bool cmp(node x,node y){    return x.x<y.x||(x.x==y.x&&x.y<y.y);}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%lf%lf",&t[i].x,&t[i].y);    sort(t+1,t+n+1,cmp);    for(int i=1;i<=n;i++)        for(int j=i+1;j<=n;j++)            q[++tot]=((t[j].y-t[i].y)/(t[j].x-t[i].x));    sort(q+1,q+tot+1);    int ans=0;    for(int i=1;i<=tot;i++)    {        if(q[i]!=q[i-1]) ans++;    }    printf("%d\n",ans);    return 0;}

1602: [Usaco2008 Oct]牧场行走

Time Limit: 5 Sec Memory Limit: 64 MB
Submit: 2176 Solved: 1155
[Submit][Status][Discuss]
Description

N头牛(2<=n<=1000)别人被标记为1到n,在同样被标记1到n的n块土地上吃草,第i头牛在第i块牧场吃草。 这n块土地被n-1条边连接。 奶牛可以在边上行走,第i条边连接第Ai,Bi块牧场,第i条边的长度是Li(1<=Li<=10000)。 这些边被安排成任意两头奶牛都可以通过这些边到达的情况,所以说这是一棵树。 这些奶牛是非常喜欢交际的,经常会去互相访问,他们想让你去帮助他们计算Q(1<=q<=1000)对奶牛之间的距离。

Input

*第一行:两个被空格隔开的整数:N和Q

*第二行到第n行:第i+1行有两个被空格隔开的整数:AI,BI,LI

*第n+1行到n+Q行:每一行有两个空格隔开的整数:P1,P2,表示两头奶牛的编号。

Output

*第1行到第Q行:每行输出一个数,表示那两头奶牛之间的距离。

Sample Input

4 2

2 1 2

4 3 2

1 4 3

1 2

3 2

Sample Output

2

7

HINT

Source

资格赛
这就是资格赛题,根本没有黄金的难
这就是道裸题
从x到y的路径长度等于deep[x]+deep[y]-2*deep[lca(x,y)]

#include<bits/stdc++.h>using namespace std;//#define Getchar getcharinline int Getchar(){    static const int  L =1<<15;    static char buf[L],*S=buf,*T=buf;    if(S==T)    {        T=(S=buf)+fread(buf,1,L,stdin);        if(S==T) return EOF;    }    return *S++;}inline int read(){    static int ch=0;    int f=1;    while(!isdigit(ch=getchar())) if(ch=='-') f=-1;    int num=ch-'0';    while(isdigit(ch=getchar())) num=(num<<1)+(num<<3)+ch-'0';    return num*f;}const int maxn = 1005;struct edge{    int to,next,val;}e[maxn<<1];int dep[maxn],fa[maxn],top[maxn],size[maxn];int h[maxn],cnt,n,m;int tot,q[maxn],son[maxn];inline void add(int from,int to,int val){    e[++cnt].next=h[from];    e[cnt].to=to;    e[cnt].val=val;    h[from]=cnt;}inline void dfs(int x){    size[x]=1;q[++tot]=x;    for(int i=h[x];i;i=e[i].next)    {        int v=e[i].to;        if(v==fa[x]) continue;        fa[v]=x;        dep[v]=dep[x]+e[i].val;        dfs(v);        size[x]+=size[v];        if(size[v]>size[son[x]]) son[x]=v;    }    return ;}void gettop(){    top[1]=1;    for(int i=2;i<=tot;i++)    {        top[q[i]]= son[fa[q[i]]]==q[i]?top[fa[q[i]]]:q[i];    }}inline int lca(int x,int y){    while(top[x]!=top[y])    {        if(dep[top[x]]<dep[top[y]]) swap(x,y);        x=fa[top[x]];    }    if(dep[x]>dep[y]) swap(x,y);    return x;}int main(){    n=read();    m=read();    int u,v,w;    for(register int i=1;i<n;i++)    {        u=read();v=read();w=read();        add(v,u,w);        add(u,v,w);    }    dfs(1);    gettop();    int x,y,l;    for(int i=1;i<=m;i++)    {        x=read();y=read();        l=lca(x,y);        printf("%d\n",dep[x]+dep[y        ]-2*dep[l]);    }    return 0;}

1601: [Usaco2008 Oct]灌水

Time Limit: 5 Sec Memory Limit: 162 MB
Submit: 2170 Solved: 1428
[Submit][Status][Discuss]
Description

Farmer John已经决定把水灌到他的n(1<=n<=300)块农田,农田被数字1到n标记。把一块土地进行灌水有两种方法,从其他农田饮水,或者这块土地建造水库。 建造一个水库需要花费wi(1<=wi<=100000),连接两块土地需要花费Pij(1<=pij<=100000,pij=pji,pii=0). 计算Farmer John所需的最少代价。

Input

*第一行:一个数n

*第二行到第n+1行:第i+1行含有一个数wi

*第n+2行到第2n+1行:第n+1+i行有n个被空格分开的数,第j个数代表pij。

Output

*第一行:一个单独的数代表最小代价.

Sample Input

4

5

4

4

3

0 2 2 2

2 0 3 3

2 3 0 4

2 3 4 0

Sample Output

9

输出详解:

Farmer John在第四块土地上建立水库,然后把其他的都连向那一个,这样就要花费3+2+2+2=9

HINT

Source

资格赛

这个题和有趣
考虑每个点只要被连接就可以,连接方式有建水库和互相连。
需要至少有一个水库
于是出现了很神的做法
我们造一个超级水库n+1,如果建水库就相当于让n+1和这个点连
那么对这图做一遍最小生成树得到的就是答案
%%%好题

#include<bits/stdc++.h>using namespace std;inline int read(){    static int ch=0;    int f=1;    while(!isdigit(ch=getchar())) if(ch=='-') f=-1;    int num=ch-'0';    while(isdigit(ch=getchar())) num=(num<<1)+(num<<3)+ch-'0';    return num*f;}const int maxn = 310;struct edge{    int from,to,val;}e[100005];int cnt,fa[maxn];int n,tot;int find(int x){    return x==fa[x]?x:fa[x]=find(fa[x]);}void add(int from,int to,int val){    e[++cnt].from=from;e[cnt].to=to;e[cnt].val=val;}bool cmp(edge x,edge y){    return x.val<y.val;}int main(){    n=read();    int u,v,w;    for(int i=1;i<=n;i++)    {        w=read();add(n+1,i,w);fa[i]=i;    }    fa[n+1]=n+1;    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)        {            w=read();            add(i,j,w);        }    sort(e+1,e+cnt+1,cmp);    int ans=0;    for(int i=1;i<=cnt;i++)    {        u=e[i].from,v=e[i].to;w=e[i].val;        int fx=find(u),fy=find(v);        if(fx!=fy)        {            tot++;            fa[fx]=fy;            ans+=w;        }        if(tot==n) break;    }    printf("%d\n",ans);}

1103: [POI2007]大都市meg

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 3031 Solved: 1588
[Submit][Status][Discuss]
Description

  在经济全球化浪潮的影响下,习惯于漫步在清晨的乡间小路的邮递员Blue Mary也开始骑着摩托车传递邮件了。
不过,她经常回忆起以前在乡间漫步的情景。昔日,乡下有依次编号为1..n的n个小村庄,某些村庄之间有一些双
向的土路。从每个村庄都恰好有一条路径到达村庄1(即比特堡)。并且,对于每个村庄,它到比特堡的路径恰好
只经过编号比它的编号小的村庄。另外,对于所有道路而言,它们都不在除村庄以外的其他地点相遇。在这个未开
化的地方,从来没有过高架桥和地下铁道。随着时间的推移,越来越多的土路被改造成了公路。至今,Blue Mary
还清晰地记得最后一条土路被改造为公路的情景。现在,这里已经没有土路了——所有的路都成为了公路,而昔日
的村庄已经变成了一个大都市。 Blue Mary想起了在改造期间她送信的经历。她从比特堡出发,需要去某个村庄,
并且在两次送信经历的间隔期间,有某些土路被改造成了公路.现在Blue Mary需要你的帮助:计算出每次送信她需
要走过的土路数目。(对于公路,她可以骑摩托车;而对于土路,她就只好推车了。)

Input

  第一行是一个数n(1 < = n < = 2 50000).以下n-1行,每行两个整数a,b(1 < = a以下一行包含一个整数m
(1 < = m < = 2 50000),表示Blue Mary曾经在改造期间送过m次信。以下n+m-1行,每行有两种格式的若干信息
,表示按时间先后发生过的n+m-1次事件:若这行为 A a b(a若这行为 W a, 则表示Blue Mary曾经从比特堡送信到
村庄a。

Output

  有m行,每行包含一个整数,表示对应的某次送信时经过的土路数目。

Sample Input

5

1 2

1 3

1 4

4 5

4

W 5

A 1 4

W 5

A 4 5

W 5

W 2

A 1 2

A 1 3

Sample Output

2

1

0

1
HINT

Source

把边权点权化其实所有点的点权都是1,如果修改了那么这个点就不是1了,就相当于让子树中所有的节点的值都减1
对于修改子树的问题我们一般都考虑dfs序,对于单点求和,区间修改肯定是树状数组喽
那么就是树状数组维护dfs序
首先初始化我们需要将dfs序记下来,考虑到题解都是进节点入一次dfs序出节点入一次dfs序,我这种辣鸡要抄所以就记两遍其实我认为可以通过记子树大小和第一次进子树的的位置就可以。
那么我们对每个区间加1,就是头+1尾部-1(因为我们多记录了一个结尾所以不是尾部加1的位置)那么如果我们要改掉一条路径就是把加的减去就好就直接头-1尾加一

#include<bits/stdc++.h>using namespace std;inline int read(){    static int ch=0;    int f=1;    while(!isdigit(ch=getchar())) if(ch=='-') f=-1;    int num=ch-'0';    while(isdigit(ch=getchar())) num=(num<<1)+(num<<3)+ch-'0';    return num*f;}const int maxn = 250005;struct edge{    int to,next;}e[maxn<<1];int h[maxn],cnt;int t[maxn<<1],n;int l[maxn],r[maxn];int tot,q;int lowbit(int x){return x&-x;}void modify(int pos,int x){    while(pos<=n*2)    {        t[pos]+=x;        pos+=lowbit(pos);    }}int sum(int x){    int res=0;    while(x)    {        res+=t[x];        x-=lowbit(x);    }    return res;}void add(int from,int to){    e[++cnt].next=h[from];    e[cnt].to=to;    //e[cnt].val=val;    h[from]=cnt;}void dfs(int x,int fa){    l[x]=++tot;    for(int i=h[x];i;i=e[i].next)    {if(e[i].to!=fa)dfs(e[i].to,x);}    r[x]=++tot;}int main(){    n=read();    int u,v;    for(int i=1;i<n;i++) u=read(),v=read(),add(u,v),add(v,u);    dfs(1,1);    for(int i=1;i<=n;i++) modify(l[i],1),modify(r[i],-1);    q=read();    char ch[2];    for(int i=1;i<n+q;i++)    {        scanf("%s",ch);        if(ch[0]=='A') {            u=read();v=read();            u=max(u,v);            modify(l[u],-1);            modify(r[u],+1);        }        else {            u=read();            printf("%d\n",sum(l[u])-1);        }    }return 0;}

1579: [Usaco2009 Feb]Revamping Trails 道路升级

Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 2229 Solved: 628
[Submit][Status][Discuss]
Description

每天,农夫John需要经过一些道路去检查牛棚N里面的牛. 农场上有M(1<=M<=50,000)条双向泥土道路,编号为1..M. 道路i连接牛棚P1_i和P2_i (1 <= P1_i <= N; 1 <= P2_i<= N). John需要T_i (1 <= T_i <= 1,000,000)时间单位用道路i从P1_i走到P2_i或者从P2_i 走到P1_i 他想更新一些路经来减少每天花在路上的时间.具体地说,他想更新K (1 <= K <= 20)条路经,将它们所须时间减为0.帮助FJ选择哪些路经需要更新使得从1到N的时间尽量少.

Input

  • 第一行: 三个空格分开的数: N, M, 和 K * 第2..M+1行: 第i+1行有三个空格分开的数:P1_i, P2_i, 和 T_i

Output

  • 第一行: 更新最多K条路经后的最短路经长度.

Sample Input

4 4 1

1 2 10

2 4 10

1 3 1

3 4 100

Sample Output

1

HINT

K是1; 更新道路3->4使得从3到4的时间由100减少到0. 最新最短路经是1->3->4,总用时为1单位. N<=10000

Source

Gold

噩梦分层图,当年刷了一页各种re,tle,mle,wa
甚至都把bzoj卡挂好几次(bzoj电脑的锅我不背)
今天终于认怂写了迪杰斯特拉(打中文是我不会拼)
暴力建图一共k+1层从下面一层去上面一层的那条边边权是0那么到最顶层的n(k*n+n)最短路显然就是免费k条路径的最小花费
想法很暴力

#include<bits/stdc++.h>using namespace std;#pragma GCC optimize (2)#pragma G++ optimize (2)//#define Getchar getcharinline int Getchar(){    static const int  L =1<<15;    static char buf[L],*S=buf,*T=buf;    if(S==T)    {        T=(S=buf)+fread(buf,1,L,stdin);        if(S==T) return EOF;    }    return *S++;}inline int read(){    static int ch=0;    while(!isdigit(ch=Getchar())) ;    int num=ch-'0';    while(isdigit(ch=Getchar())) num=(num<<1)+(num<<3)+ch-'0';    return num;}const int maxn=1e4+100;struct edge{    int to,next,val;}e[5000005];int h[maxn*22],vis[maxn*22];long long dis[maxn*22];priority_queue<pair<long long,int>,vector<pair<long long,int> >,greater<pair<long long,int > > >q;int n,m,k,cnt;void add(int from,int to,int val){    e[++cnt].next=h[from];    e[cnt].to=to;    e[cnt].val=val;    h[from]=cnt;}int main(){    n=read();    m=read();    k=read();    int u,v,w;    for(int i=1;i<=m;i++)    {        u=read();v=read();w=read();        for(int j=0;j<k;j++){            add(u+j*n,v+j*n+n,0);            add(v+j*n,u+j*n+n,0);            add(u+j*n,v+j*n,w);            add(v+j*n,u+j*n,w);        }        add(u+k*n,v+k*n,w);        add(v+k*n,u+k*n,w);    }    memset(dis,127/3,sizeof(dis));    dis[1]=0;    q.push(make_pair(0,1));    while(!q.empty())    {        int now=q.top().second;q.pop();        if(vis[now]) continue;        vis[now]=1;        for(int i=h[now];i;i=e[i].next)        {            if(dis[e[i].to]>dis[now]+(long long)e[i].val)            {                dis[e[i].to]=dis[now]+(long long)e[i].val;                q.push(make_pair(dis[e[i].to],e[i].to));            }        }    }    //for(register int i=1;i<=k*n+n;i++) printf("%d\n",dis[i]);    printf("%lld\n",dis[(k+1)*n]);    return 0;}

一上午就被颓掉了
现在我已经该去上下午的课了……
我真是个废物