[USACO 2018 Open Gold] Tutorial

Link:

传送门

A:

对于每一条分割线,设本不应在其左侧的个数为$x$

重点要发现每次一来一回的操作恰好会将一对分别应在左/右侧的一个数从右/左移过去

这样就转直接用树状数组求出最大的$x$即可

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+10;
//res至少为1
int n,bit[MAXN],res=1;P dat[MAXN];

void Update(int x)
{while(x<=n) bit[x]++,x+=x&(-x);}
int Query(int x)
{
    int ret=0;
    while(x) ret+=bit[x],x-=x&(-x);
    return ret;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&dat[i].X),dat[i].Y=i;
    sort(dat+1,dat+n+1);

    for(int i=1;i<n;i++)
        Update(dat[i].Y),res=max(res,i-Query(i));
    printf("%d",res);
    return 0;
}

Problem A

冒泡排序相关题多考虑数与数相对位置以及每个数应在的位置

B:

发现答案具有单调性后首先想到二分答案或是直接枚举

由于不用重复建图,直接枚举看起来能更快一些,不过好像并不能在线$O(logn)$判断是否出现了环……

(按照杨主力的指示还是在google上搜到了论文,不过并不打算学,传送门

剩下就是优先队列套拓扑排序就行了

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define pb push_back
typedef long long ll;
typedef pair<int,int> P;
typedef double db;
const int MAXN=1e5+10;
vector<int> dat[MAXN];
struct edge{int nxt,to;}e[MAXN<<2];
int n,m,num,x,head[MAXN],res[MAXN],in[MAXN],tot;

void add_edge(int x,int y)
{e[++tot].nxt=head[x];e[tot].to=y;head[x]=tot;}
void build(int x)
{
    tot=0;
    memset(in,0,sizeof(in));
    memset(head,0,sizeof(head));
    for(int i=1;i<=x;i++)
        for(int j=0;j<dat[i].size()-1;j++)
            add_edge(dat[i][j],dat[i][j+1]),in[dat[i][j+1]]++;
}
bool check()
{
    int sz=0;
    priority_queue<int,vector<int>,greater<int> > q;
    for(int i=1;i<=n;i++)
        if(!in[i]) q.push(i);
    while(!q.empty())
    {
        int t=q.top();q.pop();
        res[++sz]=t;
        for(int i=head[t];i;i=e[i].nxt)
        {
            in[e[i].to]--;
            if(!in[e[i].to]) q.push(e[i].to);
        }
    }
    return (sz==n);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&num);
        for(int j=1;j<=num;j++)
            scanf("%d",&x),dat[i].pb(x);
    }

    int l=1,r=m;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        build(mid);
        if(check()) l=mid+1;
        else r=mid-1;
    }
    build(r);check();
    for(int i=1;i<=n;i++)
        printf("%d ",res[i]);
    return 0;
}

Problem B

C:

明显0/1分数规划,每次$dp$找当前重量大于等于$m$的最大权值和即可

一篇很好的博客,图解0/1分数规划:传送门

顺便学了下$Dinkelbach$算法,发现就是每次找到最大值时直接跳到该方案的零点处

直到该方案的零点为当前$x$下的最大值时停止即可

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
typedef double db;
const int MAXN=1e5+10;
int n,m,w[MAXN],t[MAXN];
db l,r,a[MAXN],dp[MAXN];

bool check(db x)
{
    for(int i=1;i<=n;i++)
        a[i]=t[i]-1.0*x*w[i];
    for(int i=1;i<=m;i++) dp[i]=-1e9;    

    for(int i=1;i<=n;i++)
        for(int j=m;j>=0;j--)
            dp[min(m,j+w[i])]=max(dp[min(m,j+w[i])],dp[j]+a[i]);
    return dp[m]>=0;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&w[i],&t[i]),r+=t[i];

    while(fabs(l-r)>1e-6)
    {
        db mid=(l+r)/2.0;
        if(check(mid)) l=mid;
        else r=mid;
    }
    printf("%d",int((l+r)*500));
    return 0;
}

Problem C

原文地址:https://www.cnblogs.com/newera/p/9603979.html

时间: 2024-11-02 08:26:13

[USACO 2018 Open Gold] Tutorial的相关文章

[USACO 2016 Dec Gold] Tutorial

Link: 传送门 A: 贪心从小到大插入,用并查集维护连通性 #include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef double db; typedef long long ll; typedef pair<int,int> P; const int MAXN=1e3+10; int n,tot,cnt,f[MAXN];P dat[MAXN]; struct e

$[\ USACO\ 2018\ OPEN\ ]\ Out\ of\ Sorts\ (Gold)$

\(\\\) \(Description\) 运行以下代码对一长为\(N\)的数列\(A\)排序,不保证数列元素互异: cnt = 0 sorted = false while (not sorted): cnt = cnt + 1 sorted = true for i = 0 to N-2: if A[i+1] < A[i]: swap A[i], A[i+1] for i = N-2 downto 0: if A[i+1] < A[i]: swap A[i], A[i+1] for i

[USACO 2012 Mar Gold] Large Banner

传送门:http://www.usaco.org/index.php?page=viewproblem2&cpid=127 又是一道这种题目,遇到一次跪一次,这次终于硬着头皮看懂了题解,但是谢了代码之后还是wa了3个点(共20个),实在是找不出哪里错了,略烦... 题解真的不想写了,贴个链接叭... http://blog.csdn.net/kanosword/article/details/52585972 这道题比较冷门,官方题解看得不是太懂,上面那个链接讲得比较清楚.

bzoj2581 [USACO 2012 Jan Gold] Cow Run【And-Or Tree】

传送门1:http://www.usaco.org/index.php?page=viewproblem2&cpid=110 传送门2:http://www.lydsy.com/JudgeOnline/problem.php?id=2581 这题我一看就知道自己不会了,只想了个O(2^n * 2 ^ n)即O(2 ^ 2n)的大暴力,也懒得打了,果断看solution. 看了之后惊呆了,看到了一种从未见过,闻所未闻的树叫做And-Or Tree,百度了一下,并没有官方中文翻译,姑且叫他"

【BZOJ1229】【USACO 2008 Nov Gold】 4.Toys sadstory 三分+贪心

sad story:我们自己oj的数据貌似有点问题.标程WA了5% 题解: 复制去Google翻译翻译结果 首先引一下VFK神犇的证明来证明一下这道题是三分.. { 我来告诉你世界的真相 = = 因为这题能最小费用最大流 每次最短路长度不降 所以是单峰的 最短路长度就是差分值.. 所以一阶导不降.. 是不是简单粗暴 你要证函数是单峰的. 当然是证斜率什么的 } 三分完初始买了多少个玩具,然后就是贪心. 首先我想说这个贪心真动规.虽然它真的是贪心. 首先先说一种错误的贪心. 就是从前往后扫,优先用

USACO 2011 Jan Gold 3. Roads and Planes

题意: 给出一个n个结点m条边有向图,可能有负权边: 但是存在负权边a->b则不会有某个路径可以从b到a: 求一个源点s到所有点的最短路(无解输出"NO PATH"): n<=25000,m<=150000: 题解: 高高兴兴的写了一发spfa,O(km)嘛: 然后就TLE了,这题丧心病狂的把spfa卡掉了: 这时候理所当然的想到了dij+heap,写到一半想起来不支持负权边: 所以这个不是一个简单的单源最短路问题: 题中有一个重要条件就是负权边不会回去: 我本以为只

【POJ3659】【USACO 2008 Jan Gold】 3.Cell Phone Network 树上最小支配集/贪心 两种做法

题意:求树上最小支配集 最小支配集:点集,即每个点可以"支配"到相邻点,求最少点数可以使所有点被支配. 图上的最小支配集是NP的,但是树上的可以DP做,是O(n)的. 暴力做就好了, f[i]表示此 点被选时的子树全支配的最小代价 g[i]表示其父亲节 点被选时的子树全支配的最小代价 h[i]表示其某子节 点被选时的子树全支配的最小代价 然后暴力转移. (v是子节点) f[x]=∑(min(f[v],min(g[v],h[v])))+1; g[x]=∑(min(f[v],h[v]));

[USACO 2011 Dec Gold] Threatening Letter【后缀】

Problem 3: Threatening Letter [J. Kuipers, 2002] FJ has had a terrible fight with his neighbor and wants to send him a nasty letter, but wants to remain anonymous. As so many before him have done, he plans to cut out printed letters and paste them on

【POJ3666】【USACO 2008 Feb Gold】 2.Cow Game 动规

题意:有若干个数,然后可以花费i的代价让某个数+i或者-i. 现在要求让你把序列排成不升或者不降,问最小代价. 题解: 首先可以证明,最优花费下最后所有的数都可以是现在的某个数: 证:如果两个数调整后在两个数中间,那么可以把两个数都变为其中一个数,而代价显然是等同的. 这个出来后就好做了. 我们可以先离散化一下,然后f[i][j]表示第i个数变为j时1~i这些数保持非严格单调的最小花费 转移时f[i][j]不必n*n枚举,可以维护一个前缀最优(非常水),然后O(1)转移. 然后做一遍非升,一遍非