• USACO19DEC题解


    Bronze

    A Cow Gymnastics

    题目:https://www.luogu.com.cn/problem/P5831
    题解:用数组存一下出现位置,O(n^2)枚举一下就好。
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    #define I inline void
    #define IN inline int
    typedef long long ll;
    I read(int &res){
        re g=1;register char ch=getchar();res=0;
        while(!isdigit(ch)){
            if(ch=='-')g=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            res=(res<<3)+(res<<1)+(ch^48);
            ch=getchar();
        }
        res*=g;
    }
    int n,m,a[100][100],b[100][100],sn,ans;
    int main(){
    	freopen("gymnastics.in","r",stdin);
    	freopen("gymnastics.out","w",stdout);
    	read(m);read(n);
    	F(i,1,m){
    		F(j,1,n){
    			read(a[i][j]);
    			b[i][a[i][j]]=j;
    		}
    	}
    	F(i,1,n-1){
    		F(j,i+1,n){
    			sn=1;
    			if(b[1][i]>b[1][j]){
    				F(k,2,m)
    					if(b[k][i]<b[k][j]){
    						sn=0;
    						break;
    					}
    			}
    			else{
    				F(k,2,m){
    					if(b[k][i]>b[k][j]){
    						sn=0;
    						break;
    					}
    				}
    			}
    			ans+=sn;
    		}
    	}
    	cout<<ans;
        return 0;
    }
    

    B Where Am I?

    题目:https://www.luogu.com.cn/problem/P5832
    题解:枚举一下K,哈希一下用map一存就行。复杂度:O(n^2)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    #define I inline void
    #define IN inline int
    typedef long long ll;
    I read(int &res){
        re g=1;register char ch=getchar();res=0;
        while(!isdigit(ch)){
            if(ch=='-')g=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            res=(res<<3)+(res<<1)+(ch^48);
            ch=getchar();
        }
        res*=g;
    }
    const int Mod=1e9+7,bas=131;
    set<int>mp;
    char c[110];
    int n,m,a[110],j,s;
    int main(){
    	freopen("whereami.in","r",stdin);
    	freopen("whereami.out","w",stdout);
    	read(n);
    	scanf("%s",c+1);
    	F(i,1,n){
    		a[i]=c[i]-'A'+1;
    	}
    	F(len,1,n){
    		mp.clear();m=1;
    		F(i,1,n-len+1){
    			j=i+len-1;s=0;
    			F(k,i,j){
    				s=(ll)s*bas%Mod;
    				s+=a[k];
    			}	
    			if(mp.count(s)){
    				m=0;
    				break;
    			}
    			mp.insert(s);
    		}
    		if(m){
    			cout<<len;
    			return 0;
    		}
    	}
        return 0;
    }
    

    C Livestock Lineup

    题目:https://www.luogu.com.cn/problem/P5833
    题解:按字典序爆搜即可。当然,我写的比较麻烦,
    建了图,还要用每个点的度数分类讨论,其实复杂度
    还是一样,O(8!*8)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    #define I inline void
    #define IN inline int
    typedef long long ll;
    I read(int &res){
        re g=1;register char ch=getchar();res=0;
        while(!isdigit(ch)){
            if(ch=='-')g=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            res=(res<<3)+(res<<1)+(ch^48);
            ch=getchar();
        }
        res*=g;
    }
    string s[9]={"0","Beatrice","Belinda","Bella","Bessie","Betsy","Blue","Buttercup","Sue"};
    char c[100];
    int n,m,a[10][10],x,y,du[10],ans[10],v[10];
    I D_1(int x){
    /*	cout<<x<<" ";
    	F(i,1,x){
    		cout<<ans[i]<<" ";
    	}
    	cout<<endl;*/
    	if(x==9){
    		if(du[ans[x-1]]==2)return;
    		F(i,1,8){
    			cout<<s[ans[i]]<<endl;
    		}
    		exit(0);
    	}
    	re sn=0;
    	if(du[ans[x-1]]>=1){
    		F(i,1,8){
    			if(a[ans[x-1]][i]&&!v[i]){
    				sn++;
    			}
    		}
    		if(sn==2)return;
    		sn=1;
    		F(i,1,8){
    			if(a[ans[x-1]][i]&&!v[i]){
    				ans[x]=i,v[i]=1,D_1(x+1),v[i]=0;sn=0;	
    				break;
    			}
    		}
    		if(!sn)return;
    	}
    	F(i,1,8){
    		if(v[i])continue;
    		ans[x]=i,v[i]=1,D_1(x+1);
    		ans[x]=0;v[i]=0;
    	}
    }
    int main(){
    	freopen("lineup.in","r",stdin);
    	freopen("lineup.out","w",stdout);
    	read(n);
    	memset(a,0,sizeof(a));
    	memset(du,0,sizeof(du));
    	F(i,1,n){
    		cin>>c+1;
    		m=strlen(c+1);
    		if(m==3)x=8;
    		if(m==4)x=6;
    		if(m==6)x=4;
    		if(m==7)x=2;
    		if(m==8)x=1;
    		if(m==9)x=7;
    		if(m==5){
    			if(c[3]=='l')x=3;
    			else x=5;
    		}
    		cin>>c+1>>c+1>>c+1>>c+1>>c+1;
    		m=strlen(c+1);
    		if(m==3)y=8;
    		if(m==4)y=6;
    		if(m==6)y=4;
    		if(m==7)y=2;
    		if(m==8)y=1;
    		if(m==9)y=7;
    		if(m==5){
    			if(c[3]=='l')y=3;
    			else y=5;
    		}
    		du[x]++;du[y]++;
    		a[x][y]=1;a[y][x]=1;
    		//cout<<x<<" "<<y<<endl;
    	}
    	F(i,1,n){
    		if(du[i]<2){
    			ans[1]=i;v[i]=1;
    			D_1(2);v[i]=0;
    		}
    	}
    	//cout<<"!";
        return 0;
    }
    

    Silver

    A MooBuzz

    题目:https://www.luogu.com.cn/problem/P5834
    题解:可以明显看出,每15次,有8个数被报出来。
    随便写写就过了
    复杂度O(1)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    #define I inline void
    #define IN inline int
    typedef long long ll;
    I read(int &res){
        re g=1;register char ch=getchar();res=0;
        while(!isdigit(ch)){
            if(ch=='-')g=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            res=(res<<3)+(res<<1)+(ch^48);
            ch=getchar();
        }
        res*=g;
    }
    int n,m,ans;
    int main(){
    	freopen("moobuzz.in","r",stdin);
    	freopen("moobuzz.out","w",stdout);
    	read(n);
    	if(n%8==0){
    		ans=(n/8)*15;
    		ans--;
    		cout<<ans;
    		return 0;
    	}
    	ans=(n/8)*15;
    	n%=8;m=0;
    	F(i,1,n){
    		while(m%3==0||m%5==0)m++;
    		if(i==n){
    			ans+=m;
    			break;
    		}
    		m++;
    	}
    	cout<<ans;
        return 0;
    }
    

    B Meetings

    题目:https://www.luogu.com.cn/problem/P5835
    题解:相信这道题的两个套路大家应该都见过:
    1.不管这些牛怎么走,他们的相对位置都是不变的(没见过的一定要细细斟酌)
    2.两头牛相遇后,可以看做是“擦肩而过”,也就是并没有改变方向。所以,
    我们可以直接算出每头牛走到终点的时间。
    知道了这两个性质,做法就顺水推舟了:
    1.用类似归并排序的方法算出时刻T,由性质1,我们对时间和重量做出贡献的并不是一头牛(一定要小心);
    2.因为时间已经确定,所以可以固定向一个方向走的牛不动,相当于另一个方向走的牛走了2*T个单位距离。
    对两种方向的牛分别排序,双指针扫一下即可。具体实现看代码。
    复杂度:O(nlogn)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register ll
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    #define I inline void
    #define IN inline ll
    typedef long long ll;
    I read(ll &res){
        re g=1;register char ch=getchar();res=0;
        while(!isdigit(ch)){
            if(ch=='-')g=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            res=(res<<3)+(res<<1)+(ch^48);
            ch=getchar();
        }
        res*=g;
    }
    struct P{
    	ll x,w;
    }l[50500],r[50500],p[101000];
    ll n,m,num,tot,sum,W,X,d,Y,cnt,T,ans,L,R;
    inline bool bb1(P a,P b){
    	return a.x==b.x?a.w>b.w:a.x>b.x;
    }
    inline bool bb2(P a,P b){
    	return a.x==b.x?a.w<b.w:a.x<b.x;
    }
    IN max(ll x,ll y){
    	return x>y?x:y;
    }
    int main(){
    	freopen("meetings.in","r",stdin);
    	freopen("meetings.out","w",stdout);
    	read(n);read(m);
    	tot=sum=num=0;
    	F(i,1,n){
    		read(W);read(X);read(d);num+=W;
    		p[i].x=X;p[i].w=W;
    		if(d==1)tot++,l[tot].x=X,l[tot].w=W;
    		else sum++,r[sum].x=X,r[sum].w=W;
    	}
    	sort(l+1,l+1+tot,bb1);
    	sort(r+1,r+1+sum,bb2);
    	sort(p+1,p+1+n,bb2);
    	X=1;Y=1;cnt=0;T=0;L=1;R=n;
    	while(cnt*2<num){
    		if(m-l[X].x>r[Y].x)cnt+=p[L++].w,T=r[Y].x,Y++;
    		else cnt+=p[R--].w,T=m-l[X].x,X++;
    	}
    	//cout<<X<<" "<<Y<<" "<<T<<endl;
    	sort(l+1,l+1+tot,bb2);
    	X=1;Y=1;
    	F(i,1,sum){
    		while(X<=tot&&l[X].x<max(0ll,r[i].x-T-T))X++;
    		while(Y<=tot&&l[Y].x<=r[i].x)Y++;
    		//cout<<X<<" "<<Y<<endl;
    		ans+=(Y-X);
    	}
    	cout<<ans;
        return 0;
    }
    

    C Milk Visits

    题目:https://www.luogu.com.cn/problem/P5836
    题解:树剖+线段树板子题。详细讲解请见下一道Milk Visits。
    复杂度:O(nlog^2n)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    #define I inline void
    #define IN inline int
    typedef long long ll;
    I read(int &res){
        re g=1;register char ch=getchar();res=0;
        while(!isdigit(ch)){
            if(ch=='-')g=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            res=(res<<3)+(res<<1)+(ch^48);
            ch=getchar();
        }
        res*=g;
    }
    char c[101000];
    struct E{
    	int to,nt;
    }e[202000];
    #define T e[k].to
    char sit;
    int n,m,head[101000],fa[101000],X,Y,tot=-1,tr[101000],t[101000],siz[101000],top[101000],son[101000],id[101000],dep[101000];
    I D_1(int x,int fat,int depth){
    	siz[x]=1;dep[x]=depth;son[x]=-1;fa[x]=fat;
    	re maxi=-1;
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fat)continue;
    		D_1(T,x,depth+1);
    		siz[x]+=siz[T];
    		if(maxi<siz[T])maxi=siz[T],son[x]=T;
    	}
    }
    IN lbt(int x){
    	return x&(-x);
    }
    I modi(int x){
    	while(x<=n)tr[x]++,x+=lbt(x);
    }
    I modify(int x){
    	while(x<=n)t[x]++,x+=lbt(x);
    }
    I D_2(int x,int fat,int topi){
    	top[x]=topi;id[x]=++tot;
    	if(c[x]=='H')modi(id[x]);else modify(id[x]);
    	if(son[x]!=-1)D_2(son[x],x,topi);
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fat||T==son[x])continue;
    		D_2(T,x,T);
    	}
    }
    IN ques_sum(int x){
    	re res=0;
    	while(x)res+=tr[x],x-=lbt(x);
    	return res;
    }
    IN query(int x){
    	re res=0;
    	while(x)res+=t[x],x-=lbt(x);
    	return res;
    }
    IN ques(int x,int y,char S){
    	re res=0;
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])swap(x,y);
    		if(S=='H')res=(ques_sum(id[x])-ques_sum(id[top[x]]-1));
    		else res=(query(id[x])-query(id[top[x]]-1));
    		if(res)return 1;
    		x=fa[top[x]];
    	}
    	if(dep[x]>dep[y])swap(x,y);
    	if(S=='H')res=ques_sum(id[y])-ques_sum(id[x]-1);
    	else res=query(id[y])-query(id[x]-1);
    	if(res)return 1;
    	return 0;
    }
    int main(){
    	freopen("milkvisits.in","r",stdin);
    	freopen("milkvisits.out","w",stdout);
    	read(n);read(m);
    	cin>>c+1;
    	memset(head,-1,sizeof(head));
    	tot=-1;
    	F(i,1,n-1){
    		read(X);read(Y);
    		e[++tot].to=Y;
    		e[tot].nt=head[X];
    		head[X]=tot;
    		e[++tot].to=X;
    		e[tot].nt=head[Y];
    		head[Y]=tot;
    	}
    	D_1(1,0,1);
    	tot=0;
    	D_2(1,0,1);
    	while(m--){
    		read(X);read(Y);cin>>sit;
    		cout<<ques(X,Y,sit);
    	}
        return 0;
    }
    

    Gold

    A Milk Pumping

    题目:https://www.luogu.com.cn/problem/P5837
    题解:从1到1000枚举这个最小流量,跑最短路就好。
    复杂度:O(1000*(n+m)logn)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    #define I inline void
    #define IN inline int
    typedef long long ll;
    I read(int &res){
        re g=1;register char ch=getchar();res=0;
        while(!isdigit(ch)){
            if(ch=='-')g=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            res=(res<<3)+(res<<1)+(ch^48);
            ch=getchar();
        }
        res*=g;
    }
    struct E{
    	int to,nt,w,v;
    }e[2020];
    #define T e[k].to
    const int INF=1e9+7;
    int n,m,head[1010],X,Y,W,V,dis[1010],vis[1010],p,ans,tot;
    queue<int>q;
    int main(){
    	freopen("pump.in","r",stdin);
    	freopen("pump.out","w",stdout);
    	read(n);read(m);
    	memset(head,-1,sizeof(head));tot=-1;
    	F(i,1,m){
    		read(X);read(Y);read(W);read(V);
    		e[++tot].to=Y;
    		e[tot].nt=head[X];
    		head[X]=tot;
    		e[tot].w=W;
    		e[tot].v=V;
    		e[++tot].to=X;
    		e[tot].nt=head[Y];
    		head[Y]=tot;
    		e[tot].w=W;
    		e[tot].v=V;
    	}
    	ans=0;
    	F(val,1,1000){
    		F(i,1,n)dis[i]=INF,vis[i]=0;
    		dis[1]=0,vis[1]=1;
    		q.push(1);
    		while(!q.empty()){
    			p=q.front();q.pop();vis[p]=0;
    			for(re k=head[p];k!=-1;k=e[k].nt){
    				if(e[k].v<val)continue;
    				if(dis[p]+e[k].w<dis[T]){
    					dis[T]=dis[p]+e[k].w;
    					if(!vis[T]){
    						vis[T]=1;
    						q.push(T);
    					}
    				}
    			}
    		}
    		if(dis[n]!=INF)ans=max(ans,val*1000000/dis[n]);
    	}
    	cout<<ans;
        return 0;
    }
    

    B Milk Visits

    题目:https://www.luogu.com.cn/problem/P5838
    题解:在上一道Milk Visits中,我们用两个线段树维护了树上的信息,
    这里,我们显然不能直接开1e5棵线段树,所以考虑避免这个问题。
    Approach 1:可持久化线段树
    这个嘛。。。不会的打一下模板题就会了,这里就不讲了。
    Approach 2:离线
    遇到不强制在线,且无修改的题目,离线算法是一定要take into consideration的。
    我们将每个点的点权当做修改,和查询中每个人的喜好牛奶一起排序,按点权排序。
    这样,我们就只需要在一棵线段树上进行修改、查询操作了。每次先打个lazytag清空
    之前的信息,然后将一个点权的所有点插入线段树,然后查询。
    具体实现还是看代码。
    复杂度:O(nlog^2n)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    #define I inline void
    #define IN inline int
    typedef long long ll;
    I read(int &res){
        re g=1;register char ch=getchar();res=0;
        while(!isdigit(ch)){
            if(ch=='-')g=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            res=(res<<3)+(res<<1)+(ch^48);
            ch=getchar();
        }
        res*=g;
    }
    char c[101000];
    struct E{
    	int to,nt;
    }e[202000];
    struct Q{
    	int x,y,p,id;
    	friend bool operator < (Q a,Q b){
    		return a.p==b.p?a.id<b.id:a.p<b.p;
    	}
    }q[202000];
    #define T e[k].to
    char sit;
    int n,m,now,ans[101000],a[100100],tr[404000],laz[404000],head[101000],fa[101000],X,Y,tot=-1,siz[101000],top[101000],son[101000],id[101000],dep[101000];
    I D_1(int x,int fat,int depth){
    	siz[x]=1;dep[x]=depth;son[x]=-1;fa[x]=fat;
    	re maxi=-1;
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fat)continue;
    		D_1(T,x,depth+1);
    		siz[x]+=siz[T];
    		if(maxi<siz[T])maxi=siz[T],son[x]=T;
    	}
    }
    I D_2(int x,int fat,int topi){
    	top[x]=topi;id[x]=++tot;
    	q[tot].x=x;q[tot].y=id[x];q[tot].p=a[x];q[tot].id=-1;
    	//将每个点看做修改,id为负是为了使其排序时排在查询前面 
    	if(son[x]!=-1)D_2(son[x],x,topi);
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fat||T==son[x])continue;
    		D_2(T,x,T);
    	}
    }
    #define all 1,1,n
    #define lt k<<1,l,mid
    #define rt k<<1|1,mid+1,r
    I modi(int k,int l,int r,int x){
    	if(l==r){
    		tr[k]=1;
    		return;
    	}
    	if(laz[k])tr[k<<1]=tr[k<<1|1]=laz[k]=0,laz[k<<1]=laz[k<<1|1]=1;
    	re mid=(l+r)>>1;
    	if(x<=mid)modi(lt,x);
    	else modi(rt,x);
    	tr[k]=tr[k<<1]|tr[k<<1|1];
    }
    IN ques_sum(int k,int l,int r,int x,int y){
    	if(x>r||y<l)return 0;
    	if(x<=l&&r<=y)return tr[k];
    	if(laz[k])tr[k<<1]=tr[k<<1|1]=laz[k]=0,laz[k<<1]=laz[k<<1|1]=1;
    	re mid=(l+r)>>1;
    	return ques_sum(lt,x,y)|ques_sum(rt,x,y);
    }
    IN ques(int x,int y){
    	re res=0;
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])swap(x,y);
    		res|=ques_sum(all,id[top[x]],id[x]);
    		if(res)return 1;
    		x=fa[top[x]];
    	}
    	if(dep[x]>dep[y])swap(x,y);
    	res|=ques_sum(all,id[x],id[y]);
    	if(res)return 1;
    	return 0;
    }
    int main(){
    	freopen("milkvisits.in","r",stdin);
    	freopen("milkvisits.out","w",stdout);
    	read(n);read(m);
    	F(i,1,n){
    		read(a[i]);
    	}
    	memset(head,-1,sizeof(head));
    	tot=-1;
    	F(i,1,n-1){
    		read(X);read(Y);
    		e[++tot].to=Y;
    		e[tot].nt=head[X];
    		head[X]=tot;
    		e[++tot].to=X;
    		e[tot].nt=head[Y];
    		head[Y]=tot;
    	}
    	D_1(1,0,1);
    	tot=0;
    	D_2(1,0,1);
    	F(i,1,m){
    		tot++;
    		read(q[tot].x);read(q[tot].y);read(q[tot].p);q[tot].id=i;
    	}
    	sort(q+1,q+1+tot);
    	now=q[1].p;
    	F(i,1,tot){
    		if(q[i].p>now)laz[1]=1,tr[1]=0,now=q[i].p;//点权变化时,清空线段树,打懒标记是一个不错的处理方式 
    		if(q[i].id<0)modi(all,q[i].y);
    		else ans[q[i].id]=ques(q[i].x,q[i].y);
    	}
    	F(i,1,m){
    		cout<<ans[i];
    	}
        return 0;
    }
    

    C Moortal Cowmbat

    题目:https://www.luogu.com.cn/problem/P5839
    题解:首先观察到m很小,先O(m^3)floyd判一下两两之间的最小代价。
    设f[i][j]表示处理了前i个字符,最后一堆字符为j的最小代价。
    可以口胡一下转移方程
    暴力转移是O(n^2m^2)的,我们无法接受。
    考虑优化这个dp。

    O(n^2m)

    考虑到当前的转移并不需要知道上一次的最后一段的字母是什么,我们
    可以先对i的所有的f[i][j]取个min,再进行转移。

    O(nm)

    发现对于当前的i,可以转移到它的p是从1开始的一段连续区间。
    我们设f[i]表示处理完前i个字母的最小代价,sum[i][j]表示从1到i,
    将所有字母都变为j的代价。
    这样,转移方程就可以写为:
    f[i]=min{f[p]+sum[i][j]-sum[p][j},p (in) [1,i-k]
    枚举特定的j时,可以将sum[i][j]从这个max里提出来。
    发现只需要取f[p]-sum[p][j]的min。
    我们设mx[j]表示对于p (in) [1,i-k],f[p]-sum[p][j]的最小值。
    这样,转移就变成了O(1)。具体实现还有一些小细节,看代码吧~
    复杂度:O(m^3+nm)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    #define I inline void
    #define IN inline int
    typedef long long ll;
    I read(int &res){
        re g=1;register char ch=getchar();res=0;
        while(!isdigit(ch)){
            if(ch=='-')g=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            res=(res<<3)+(res<<1)+(ch^48);
            ch=getchar();
        }
        res*=g;
    }
    const int INF=1e9+7;
    char c[101000];
    int n,m,k,a[101000],dis[30][30],mx[30],sum[101000][30],f[101000];
    int main(){
    	freopen("cowmbat.in","r",stdin);
    	freopen("cowmbat.out","w",stdout);
    	read(n);read(m);read(k);
    	cin>>c+1;
    	F(i,1,n){
    		a[i]=c[i]-'a'+1;
    	}
    	F(i,1,m){
    		F(j,1,m){
    			read(dis[i][j]);
    		}
    	}
    	F(k,1,m){
    		F(i,1,m){
    			F(j,1,m){
    				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);//Floyd
    			}
    		}
    	}
    	memset(mx,0,sizeof(mx));
    	memset(sum,0,sizeof(sum));
    	F(i,1,k-1){
    		F(j,1,m){
    			sum[i][j]=sum[i-1][j]+dis[a[i]][j];//预处理sum 
    		}
    	}
    	F(i,k,n){
    		f[i]=INF;
    		if(i>=(k<<1)){
    			F(j,1,m){
    				mx[j]=min(mx[j],f[i-k]-sum[i-k][j]);//i<2k时不存在合法的f 
    			}
    		}
    		F(j,1,m){
    			sum[i][j]=sum[i-1][j]+dis[a[i]][j];
    			f[i]=min(f[i],mx[j]+sum[i][j]);
    		}
    	}
    	cout<<f[n];
        return 0;
    }
    

    Platinum

    A Greedy Pie Eaters

    题目:http://www.usaco.org/index.php?page=viewproblem2&cpid=972
    题目大意:有n个派,m头牛,每头牛有一个区间[l,r]和一个权值w。
    没有两头牛有相同的[l,r]。
    你需要选择一些牛,按一定顺序吃派。每头牛会吃完他的那个区间的所有派。
    问在满足每头牛都至少有一个派吃的情况下,能获得的最大权值是多少。
    n<=300,m<=45000
    题解:n很小,可以想到区间DP。
    设f[i][j]表示吃掉的派是[i,j]的子集的最大权值。
    显然,f[i][j]=max{f[i][k-1]+f[k][j]},k(in)[i,j]
    但这样的转移不能加入新的牛。
    可以考虑待合并的两个状态f[i][k-1]和f[k+1][j],此时k这个派还没有牛动。
    我们设mx[k][i][j]表示满足i<=l[k]<=k<=r[k]<=j的所有牛的最大权值。
    那么加入新牛的转移方程就出炉了:
    f[i][j]=max{f[i][k-1]+mx[k][i][j]+f[k+1][j]},k(in)(i,j)
    复杂度O(n^3)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    typedef long long ll;
    #define I inline void
    #define IN inline int
    template<class D>I read(D &res){
    	res=0;register D g=1;register char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')g=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		res=(res<<3)+(res<<1)+(ch^48);
    		ch=getchar();
    	}
    	res*=g;
    }
    int n,m,mx[330][330][330],f[330][330],W,X,Y;
    int main(){
    	freopen("pieaters.in","r",stdin);
    	freopen("pieaters.out","w",stdout);
    	read(n);read(m);
    	memset(mx,0,sizeof(mx));
    	F(i,1,m){
    		read(W);read(X);read(Y);
    		F(j,X,Y)mx[j][X][Y]=max(W,mx[j][X][Y]);
    	}
    	F(i,1,n){
    		FOR(j,i,1){
    			F(k,i,n){
    				if(j>1)mx[i][j-1][k]=max(mx[i][j-1][k],mx[i][j][k]);
    				if(k<n)mx[i][j][k+1]=max(mx[i][j][k+1],mx[i][j][k]);
    			}
    		}
    	}
    	FOR(i,n,1){
    		F(j,i,n){
    			F(k,i,j-1)f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
    			F(k,i,j)f[i][j]=max(f[i][j],mx[k][i][j]+(k>i?f[i][k-1]:0)+(k<j?f[k+1][j]:0));
    		}
    	}
    	printf("%d",f[1][n]);
    	return 0;
    }
    

    B Bessie's Snow Cow

    题目:http://www.usaco.org/index.php?page=viewproblem2&cpid=973
    题目大意:给你一棵树,要求支持两种操作:
    1.给定x,c,给x的子树染上颜色c(一个节点可以有多个颜色)
    2.给定x,求x的子树的所有节点的所有颜色个数和(每个节点单独算)
    题解:这个题有两个难点:
    1.修改时,如何更新其对子树内节点的贡献,以及如何上传对祖先的影响;
    2.查询时,如何一次性统计子树内所有节点的贡献
    我们发现,如果将一个操作的复杂度变为O(logn)甚至O(1),另一个操作的
    复杂度将不可避免地到达O(n)。
    考虑统计答案。答案可以分为两个部分:一是x或x的祖先已经染过的颜色,
    这些的贡献可以一并计算;二是子树内染色的情况。
    对于第一个,我们在每次修改时区间加就行了,当然还要撤销之前子树内
    的一些修改操作的贡献。
    我们以这棵树的dfs序为下标开一个树状数组,用差分维护每个节点已经染
    上的颜色个数,再开1e5个set记录下每个颜色的染色情况,避免重复染色。
    每次修改操作步骤如下:
    1.判断x的祖先是否已经被染上c色。若已经染色,直接退出;
    2.查询x子树内是否有节点染上c色。若已经染色,撤销其影响并在set中
    删除此节点;
    3.将此次修改加入set,进行区间修改。
    那对于第二个,又该怎么求呢?
    因为这些贡献是一段dfs序连续区间的单点贡献,我们可以另开一个树状数组
    记录这个贡献。每次修改只需要单点+siz[x]即可。
    修改操作的步骤同上。每次只需要查询[id[x]+1,id[x]+siz[x]-1]这段区间的
    贡献即可(也就是x子树除去x节点的贡献)
    设两个查询得到的值是ans1和ans2,那么答案即为:siz[x]*ans1+ans2
    复杂度:因为每次修改只会被加入和撤销一次,每次都是O(logn),
    查询也是O(logn)的,所以总复杂度即为O(nlogn)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register ll
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    typedef long long ll;
    #define I inline void
    #define IN inline ll
    template<class D>I read(D &res){
    	res=0;register D g=1;register char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')g=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		res=(res<<3)+(res<<1)+(ch^48);
    		ch=getchar();
    	}
    	res*=g;
    }
    const ll INF=1e9+7;
    struct E{
    	ll to,nt;
    }e[202000];
    #define T e[k].to
    ll n,m,tot,sit,X,Y,head[101000],siz[101000],id[101000],p[101000],out[101000];
    struct BIT{
    	ll tr[101000],res;
    	IN lbt(ll x){
    		return x&(-x);
    	}
    	I modi(ll x,ll w){while(x<=n){tr[x]+=w,x+=lbt(x);}}
    	IN ques(ll x){res=0;while(x){res+=tr[x],x-=lbt(x);}return res;}
    }A,B;
    set<ll>s[101000];
    I modify(ll x,ll sn){
    	A.modi(id[x],sn);
    	A.modi(out[x]+1,-1*sn);
    	B.modi(id[x],sn*siz[x]);
    	//cout<<id[x]<<" "<<out[x]<<" "<<sn<<endl;
    }
    I D_1(ll x,ll fa){
    	id[x]=++tot;siz[x]=1;p[tot]=x;
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa)continue;
    		D_1(T,x);siz[x]+=siz[T];
    	}
    	out[x]=tot;
    }
    int main(){
    	freopen("snowcow.in","r",stdin);
    	freopen("snowcow.out","w",stdout);
    	read(n);read(m);
    	memset(head,-1,sizeof(head));
    	tot=-1;
    	F(i,1,n-1){
    		read(X);read(Y);
    		e[++tot].to=Y;
    		e[tot].nt=head[X];
    		head[X]=tot;
    		e[++tot].to=X;
    		e[tot].nt=head[Y];
    		head[Y]=tot;
    	}
    	F(i,0,100000)s[i].insert(INF);
    	tot=0;
    	D_1(1,0);
    	//F(i,1,n)cout<<id[i]<<" "<<siz[i]<<" "<<out[i]<<endl;
    	while(m--){
    		read(sit);read(X);
    		if(sit==1){
    			read(Y);
    			auto P=s[Y].upper_bound(id[X]);
    			if(P!=s[Y].begin()&&out[p[*prev(P)]]>=out[X])continue;
    			while(1){
    				P=s[Y].upper_bound(id[X]);
    				if(*P>out[X])break;
    				modify(p[*P],-1);
    				s[Y].erase(P);
    			}
    			s[Y].insert(id[X]);
    			modify(X,1);
    		}
    		else printf("%lld
    ",siz[X]*A.ques(id[X])+B.ques(out[X])-B.ques(id[X]));//printf("%d %d %d %d %d %lld
    ",id[X],out[X],A.ques(id[X]),B.ques(out[X]),B.ques(id[X]),(ll)siz[X]*A.ques(id[X])+(ll)B.ques(out[X])-(ll)B.ques(id[X]));
    	}
    	return 0;
    }
    

    C Tree Depth

    待填坑~

  • 相关阅读:
    解题:POI 2009 TAB
    解题:POI 2015 Pieczęć
    解题:POI 2013 Taxis
    解题:POI 2015 Kinoman
    题目1012:畅通工程(并查集)
    并查集深入分析
    题目1186:打印日期(日期计算)
    C/C++如何整行读入字符串?
    四种方法解决最大连续子序列和问题
    题目1011:最大连续子序列
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/12073907.html
Copyright © 2020-2023  润新知