• USACO 2019 January Contest, Platinum 题解


    Problem 1. Redistricting

    (H)看做(+1)(G)看做(-1),得到一个前缀和数组(sum_i)

    (dp_i)为考虑到(i)时的答案,有(dp_i=min(dp_j+[sum_i-sum_jleq 0]))

    直接(dp)时间是(O(nk))的,使用单调队列优化时间复杂度降为(O(nlog_2k))

    注意单调队列是双关键字的,第一关键字是(dp),第二是(sum)

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<math.h>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    using namespace std;
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define fir first
    #define sec second
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define maxd 1000000007
    #define eps 1e-6
    typedef long long ll;
    const int N=100000;
    const double pi=acos(-1.0);
    int n,k,dp[300300],sum[300500];
    char s[300500];
    struct hnode{int val,id;};
    bool operator <(hnode p,hnode q)
    {
    	if (p.val==q.val) return sum[p.id]>sum[q.id];
    	else return p.val>q.val;
    }
    priority_queue<hnode> q;
    
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    	while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    	return x*f;
    }
    
    int main()
    {
    	freopen("redistricting.in","r",stdin);
    	freopen("redistricting.out","w",stdout);
    	n=read();k=read();
    	scanf("%s",s+1);
    	rep(i,1,n) 
    		if (s[i]=='H') sum[i]=sum[i-1]+1;else sum[i]=sum[i-1]-1;
    	memset(dp,0x3f,sizeof(dp));dp[0]=0;
    	q.push((hnode){0,0});
    	rep(i,1,n)
    	{
    		while (q.top().id<i-k) q.pop();
    		dp[i]=q.top().val;
    		if (sum[q.top().id]>=sum[i]) dp[i]++;
    		q.push((hnode){dp[i],i});
    	}
    	printf("%lld",dp[n]);
    	return 0;
    }
    

    Problem 2. Exercise Route

    对于一条非树边((u_1,v_1)),将它加入一棵树后树上刚好出现一个环

    要使得加入两条非树边((u_1,v_1),(u_2,v_2))后形成一个包含这两条边的环,则原来的两个小环路径必有交

    问题转化成有多少对((u_1,v_1).(u_2,v_2))满足在树上的两条路径有交边

    先考虑一个一维的问题:给出(n)个区间([L_i,R_i]),询问有多少对区间有交集

    做法:对于每个(L_x),答案减去(L_i<L_x)的区间数;对于每个(R_x),答案加上(L_I<R_x)的区间数,这样的话对于一个区间([L_x,R_x]),我们统计了所有的(L_xleq L_ileq R_x)的区间,符合区间交的定义

    实现:我们类比差分的思想,在(L_i)处打上(+1)(tag),之后做一遍前缀和。注意预处理掉(L_i=L_j)的情况

    在树上的话,我们将路径((u,v))拆成((u,lca))((v,lca)),这样做一遍树上差分+树上前缀和即可

    具体的,对于一条路径((u,lca)),我们找到(lca)在该路径上的儿子(p),在(p)上打一个(+1tag)即可

    注意避免重复计数

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<math.h>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    using namespace std;
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define fir first
    #define sec second
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define maxd 1000000007
    #define eps 1e-6
    #define mp make_pair
    typedef long long ll;
    typedef pair<int,int> pii;
    const int N=100000;
    const double pi=acos(-1.0);
    struct node{int to,nxt;}sq[400200];
    int all=0,head[200200];
    int n,m,sp[200200][2],fa[200200][20],dep[200200],lca[200200];
    ll tag[200200],val[200200];
    map<pii,int> tax;
    
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    	while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    	return x*f;
    }
    
    void add(int u,int v)
    {
    	all++;sq[all].to=v;sq[all].nxt=head[u];head[u]=all;
    }
    
    void dfs1(int u,int fu)
    {
    	dep[u]=dep[fu]+1;fa[u][0]=fu;
    	rep(i,1,19)
    		fa[u][i]=fa[fa[u][i-1]][i-1];
    	int i;
    	for (i=head[u];i;i=sq[i].nxt)
    	{
    		int v=sq[i].to;
    		if (v!=fu) dfs1(v,u);
    	}
    }
    
    int LCA(int u,int v)
    {
    	if (dep[u]<dep[v]) swap(u,v);
    	int tmp=dep[u]-dep[v];
    	per(i,19,0)
    		if ((tmp>>i)&1) u=fa[u][i];
    	if (u==v) return u;
    	per(i,19,0)
    		if (fa[u][i]!=fa[v][i]) {u=fa[u][i];v=fa[v][i];}
    	return fa[u][0];
    }
    
    int nxt(int u,int fu)
    {
    	if (u==fu) return -1;
    	per(i,19,0)
    		if ((fa[u][i]) && (dep[fa[u][i]]>dep[fu])) u=fa[u][i];
    	return u;
    }
    
    void dfs2(int u,int fu)
    {
    	val[u]=val[fu]+tag[u];int i;
    	for (i=head[u];i;i=sq[i].nxt)
    	{
    		int v=sq[i].to;
    		if (v!=fu) dfs2(v,u);
    	}
    }
    
    int main()
    {
    	freopen("exercise.in","r",stdin);
    	freopen("exercise.out","w",stdout);
    	n=read();m=read();
    	rep(i,1,n-1)
    	{
    		int u=read(),v=read();
    		add(u,v);add(v,u);
    	}
    	dfs1(1,0);ll ans=0;
    	rep(i,n,m)
    	{
    		sp[i][0]=read();sp[i][1]=read();
    		lca[i]=LCA(sp[i][0],sp[i][1]);
    		int nx=nxt(sp[i][0],lca[i]),ny=nxt(sp[i][1],lca[i]);
    		if (nx!=-1) {tag[nx]++;ans-=tag[nx];}
    		if (ny!=-1) {tag[ny]++;ans-=tag[ny];}
    		if ((nx!=-1) && (ny!=-1))
    		{
    			if (nx>ny) swap(nx,ny);
    			ans-=tax[mp(nx,ny)];tax[mp(nx,ny)]++;
    		}//防止(u_1,v_1)(u_2,v_2)被记做2组
    	}
    	dfs2(1,0);
    	rep(i,n,m)
    	{
    		int u=sp[i][0],v=sp[i][1];
    		ans+=(val[u]+val[v]-val[lca[i]]*2);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    		
    

    Problem 3. Train Tracking 2

    具有一定思维难度的(dp)

    记所有数为(a_1,a_2,cdots,a_n)

    注意到当(c_i eq c_{i+1})时,我们可以确定某些数,这启发着我么去考虑一段(c)值相同的连续段

    假设我们有一个极大连续段(c_i=c_{i+1}=cdots=c_j=v),那么它们能影响到的数一共有(j-i+k)

    (f_i)表示强制(a_i=v)时的方案数,记(p=10^9-v),暴力(dp)的话就是枚举上一个为(v)的位置,即(f_i=sum_{j=i-k}^{i-1}p^{i-j-1}f_j),显然超时

    优化的话考虑将上面的那个式子进行错位相减,得到(f_i=(p+1)f_{i-1}-p^kf_{i-k-1})。这个式子的实际意义时,第(i-1)个数可以任意取(不必限制为(v))的方案数,减去([i-1,i-k])中的数均大于(v)的不合法的方案数,时间复杂度(O(n)),可以接受

    接下来就是防止(a_i)被重复考虑或者未被考虑了,若(c_{i-1}>c_i)可以看做是(a_{i+k-1})已被确定,同时它以前的数已在(c_{i-1})中被考虑到(因为(a_{i+k-2}...a_igeq c_{i-1}>c_i)),此时计算的(a)的长度就会减少一个(k),对于(c_{j+1}>c_j)的情况也是如此

    最后就是长度为(len)的序列的答案应为(f_{len+1}),因为我们并未强制(a)的最后一位是(v)

    代码十分简单

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<math.h>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    using namespace std;
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define fir first
    #define sec second
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define maxd 1000000007
    #define eps 1e-6
    typedef long long ll;
    const int N=1000000000;
    const double pi=acos(-1.0);
    int n,k,a[100100];
    ll dp[100100];
    
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    	while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    	return x*f;
    }
    
    ll qpow(ll x,int y)
    {
    	ll ans=1;
    	while (y)
    	{
    		if (y&1) ans=ans*x%maxd;
    		x=x*x%maxd;y>>=1;
    	}
    	return ans;
    }
    
    ll solve(int val,int len)
    {
    	dp[0]=1;dp[1]=1;
    	int i;ll p=N-val,tmp=qpow(p,k);
    	rep(i,2,len+1)
    	{
    		dp[i]=(p+1)*dp[i-1]%maxd;
    		if (i-k-1>=0) dp[i]=(dp[i]-tmp*dp[i-k-1]%maxd+maxd)%maxd;
    	}
    	return dp[len+1];
    }
    
    int main()
    {
    	freopen("tracking2.in","r",stdin);
    	freopen("tracking2.out","w",stdout); 
    	n=read();k=read();
    	rep(i,1,n-k+1) a[i]=read();
    	int ans=1,i,j=0;
    	for (i=1;i<=n-k+1;i=j+1)
    	{
    		j=i;
    		while ((j<=n-k+1) && (a[i]==a[j])) j++;j--;
    		int len=j+k-i;
    		if ((i>1) && (a[i]<a[i-1])) len-=k;
    		if ((j<n-k+1) && (a[j]<a[j+1])) len-=k;
    		if (len>0) ans=ans*solve(a[i],len)%maxd;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    Redis 系列之CentOS下Redis的安装
    Redis 系列之CentOS下Redis的安装
    Redis 系列之CentOS下Redis的安装
    Redis 系列之CentOS下Redis的安装
    55种网页常用小技巧
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/encodetalker/p/11234249.html
Copyright © 2020-2023  润新知