[USACO 2017 Dec Gold] Tutorial

Link:

USACO 2017 Dec Gold 传送门

A:

为了保证复杂度明显是从终结点往回退

结果一开始全在想优化建边$dfs$……其实可以不用建边直接$multiset$找可行边跑$bfs$就行了

由于保证每个点只进队列一次、被搜索到一次,因此复杂度为$O(n*log(n))$

#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=2e5+10;
queue<int> q;
multiset<P>::iterator it;
multiset<P> sa,sb;
int n,d,dist[MAXN],a[MAXN],b[MAXN];

int main()
{
    scanf("%d%d",&n,&d);
    for(int i=1;i<=2*n;i++) 
        scanf("%d%d",&a[i],&b[i]),a[i]=-a[i],b[i]=-b[i],dist[i]=-1;
    for(int i=1;i<=n;i++)
    {
        if(b[i]==0) q.push(i),dist[i]=1;
        else sa.insert(P(b[i],i));
        if(a[n+i]==0) q.push(n+i),dist[n+i]=1;
        else sb.insert(P(a[n+i],n+i));
    }    
    
    while(!q.empty())
    {
        int t=q.front();q.pop();
        if(t<=n)
        {
            while(true)
            {
                it=sb.lower_bound(P(a[t],0));
                if(it==sb.end()||(*it).X-a[t]>d) break;
                dist[(*it).Y]=dist[t]+1;
                q.push((*it).Y);sb.erase(it);
            }
        }
        else
        {
            while(true)
            {
                it=sa.lower_bound(P(b[t],0));
                if(it==sa.end()||(*it).X-b[t]>d) break;
                dist[(*it).Y]=dist[t]+1;
                q.push((*it).Y);sa.erase(it);
            }
        }
    }
    for(int i=1;i<=n;i++)
        printf("%d\n",dist[i]);    
    return 0;
}
Problem A

 

B:

将无根树转化为有根树方便计数

明显树形$dp$,转移$dp[i][j]=\prod_{k\in son} dp[k][(j+1)mod3]+dp[k][(j+2)mod3]$

#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,MOD=1e9+7;
struct edge{int nxt,to;}e[MAXN<<2];
int n,k,x,y,head[MAXN],tot;ll dp[MAXN][3];

void add_edge(int x,int y)
{e[++tot].nxt=head[x];e[tot].to=y;head[x]=tot;}
void dfs(int x,int anc)
{
    for(int i=head[x];i;i=e[i].nxt)
    {
        if(e[i].to==anc) continue;
        dfs(e[i].to,x);
        for(int j=0;j<3;j++)
            (dp[x][j]*=dp[e[i].to][(j+1)%3]+dp[e[i].to][(j+2)%3])%=MOD;
    }
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<n;i++)
        scanf("%d%d",&x,&y),add_edge(x,y),add_edge(y,x);
    for(int i=1;i<=n;i++) 
        dp[i][0]=dp[i][1]=dp[i][2]=1;
    for(int i=1;i<=k;i++)
        scanf("%d%d",&x,&y),y--,dp[x][(y+1)%3]=dp[x][(y+2)%3]=0;
    
    dfs(1,0);
    printf("%lld",(dp[1][0]+dp[1][1]+dp[1][2])%MOD);
    return 0;
}
Problem B

 

C:

明显一个序列的和到达$m$后再进行扩展不会使答案更优

于是想到用$two$ $pointers$找到所有刚刚达到$m$的区间,并用$multiset$维护最大值

#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;
multiset<int,greater<int> > mx;ll sum,m;
int n,f[MAXN],s[MAXN],res=1<<30;

int main()
{
    scanf("%d%lld",&n,&m);
    for(int i=1;i<=n;i++) 
        scanf("%d%d",&f[i],&s[i]);
    
    int lst=0;
    for(int i=1;i<=n;i++)
    {
        while(sum<m&&lst<=n)
            sum+=f[++lst],mx.insert(s[lst]);
        if(lst>n) break;
        res=min(res,*mx.begin());
        //multiset.erase(val)会删掉所有相同元素! 
        //因此要用multiset.erase(it)来删除 
        sum-=f[i];mx.erase(mx.find(s[i]));
    }
    printf("%d",res);
    return 0;
}
Problem C

注意:

1、可能有重复值因此要用$multiset$而非$set$

2、$multiset.erase(val)$会删掉所有相同元素,因此要用$multiset.erase(it)$来删除

3、使用$two$ $pointers$时注意末尾只删除不增加的情况

 

posted @ 2018-09-04 09:19  NewErA  阅读(169)  评论(0编辑  收藏  举报