zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 007题解

    传送门

    (A)

    咕咕咕

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=15;
    char mp[N][N];int vis[N][N],n,m;
    bool dfs(int x,int y){
    	vis[x][y]=1;
    	if(x==n&&y==m)return true;
    	R int c=0;
    	if(x<n&&mp[x+1][y]=='#')++c;
    	if(y<m&&mp[x][y+1]=='#')++c;
    	if(c!=1)return false;
    	if(x<n&&mp[x+1][y]=='#')return dfs(x+1,y);
    	if(y<m&&mp[x][y+1]=='#')return dfs(x,y+1);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	fp(i,1,n)scanf("%s",mp[i]+1);
    	if(mp[1][1]!='#'||mp[n][m]!='#')return puts("Impossible"),0;
    	if(!dfs(1,1))return puts("Impossible"),0;
    	fp(i,1,n)fp(j,1,m)if((mp[i][j]=='#')!=vis[i][j])return puts("Impossible"),0;
    	return puts("Possible"),0;
    }
    

    (B)

    先把(a)(b)分别设成(1)(n)来满足第一第二个条件

    我们发现,如果把(a_i)(a_n)全部加上(x),且(b_1)(b_i)全部加上(x),可以使(a_i+b_i)相对增加(x)且仍满足第一第二个条件

    那么我们令(p_i)相对增加(i)即可满足第三个条件,最大的数是(O(n^2))级别的,不会超过(10^9)

    const int N=1e5+5;
    int a[N],b[N],p[N],n;
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&p[i]),a[p[i]]+=i,b[p[i]]+=i;
    	fp(i,1,n)a[i]+=a[i-1];fd(i,n,1)b[i]+=b[i+1];
    	fp(i,1,n)printf("%d%c",a[i]+i," 
    "[i==n]);
    	fp(i,1,n)printf("%d%c",b[i]+n-i+1," 
    "[i==n]);
    	return 0;
    }
    

    (C)

    完全想不到啊……

    首先我们转化为有(2n+1)个物品,每次随机删去相邻的两个,求距离的期望和

    我们发现一开始时距离是一个等差数列,并且如果维护每两个点间距离的期望,那么一次操作之后仍然是等差数列

    具体的证明的话,如果删去的两个点在边界上显然操作之后还是等差,否则如果对于一个长度为(d+kx)的段,删去这段的两个顶点之后这一段长度变为(3d+3kx),所以令(d_i)表示第(i)个点和第(i+1)个点的期望距离,(d_i')为操作之后的期望距离,那么总共有(2n)种删法,所以(d_i'={iover 2n}(d_i+2x)+{1over 2n}(3d_i+3x)+{2n-i-1over 2n}d_i={2n+2over 2n}d_i+{2i+3over 2n}x),所以(x'=d_i'-d_{i-1}'={2n+4over 2x}x={n+2over n}x),是一个定值

    然后再来考虑新的等差数列的首项,如果删了(1,2),首项变为(d+2x),如果删了(2,3),首项变为(3x+3d),否则不变,所以新的首项(d_1^{'}=frac{1}{2n}*(d_1+2x)+frac{1}{2n}*(3d_1+3x)+frac{2n-2}{2n}*d_1=frac{(2n+2)d_1+5x}{2n})

    然后每次计算即可

    int n;double d,x,res;
    int main(){
    	scanf("%d%lf%lf",&n,&d,&x);
    	for(;n;--n){
    		res+=(d+((n<<1)-1)*x*0.5),
    		d=(((n<<1)+2)*d+5*x)/(n<<1),
    		x=(n+2)*x/n;
    	}
    	printf("%.12lf
    ",res);
    	return 0;
    }
    

    (D)

    首先我们容易写出一个暴力,设(f_i)表示把前(i)个数全部处理完的最小时间,则(f_i=min_{j<i}(f_j+max(t,(a_i-a_{j+1}))))

    因为(max(t,(a_i-a_{j+1}))的分界点是随(i)递增而不降的,所以我们可以直接把分界点两边的答案分别用(set)记录下来然后直接查询就好了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int N=5e5+5;const ll inf=1e18;
    int n,t,e,a[N];ll f[N];
    inline ll min(R ll x,R ll y){return x<y?x:y;}
    inline ll max(R ll x,R ll y){return x>y?x:y;}
    struct Queue{
    	priority_queue<ll,vector<ll>,greater<ll> >A,B;
    	inline void push(R ll x){A.push(x);}
    	inline void pop(R ll x){B.push(x);}
    	inline ll top(){
    		while(!B.empty()&&A.top()==B.top())A.pop(),B.pop();
    		return A.empty()?inf:A.top();
    	} 
    }s1,s2;
    int main(){
    	scanf("%d%d%d",&n,&e,&t);
    	fp(i,1,n)scanf("%d",&a[i]);
    	s2.push(0);
    	for(R int i=1,j=0;i<=n;++i){
    		while(j<i&&((a[i]-a[j+1])<<1)>=t)s1.push(f[j]-(a[j+1]<<1)),s2.pop(f[j]),++j;
    		f[i]=inf,cmin(f[i],s1.top()+(a[i]<<1)),cmin(f[i],s2.top()+t);
    		s2.push(f[i]);
    	}
    	printf("%lld
    ",f[n]+e);
    	return 0;
    }
    

    (E)

    首先肯定要二分答案,且因为每条边只能经过两次,所以进入一颗子树之后必须遍历完其中所有节点才能出来

    对于每一个点,记录一个状态((a,b))表示一条合法的路径是花费(a)的代价从当前节点走到某个叶子,花费(b)的代价从某个叶子走回到当前节点,那么对于左子树的((a,b))和右子树的((c,d)),只有当(b+w_i+c+w_jleq mid)的时候它们才能合并成((a+w_i,d+w_j)),同理也要判断一下是否能反过来合并成((c+w_j,b+w_i))

    每一次把左右儿子的状态进行合并,最后只要(1)号点存在合法的状态就说明(mid)可行

    有一个优化,就是对于一个合法的(a)来说,它对应的(b)一定是越小越好,那么我们可以对于左子树按第一维排序,右子树按第二位排序,这样合并的复杂度就可以优化到(O(n))

    然而状态数可能会比较多,所以我们每一次用状态数小的去合并状态数大的,这样状态数(S_uleq 2min(S_i,S_j)),总的状态数是(O(nlog n))的,于是总复杂度为(O(nlog^2 n))

    然而比较奇怪的是我如果用状态数大的去合并小的不是(T)而是(WA)……好迷啊……

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define pb push_back
    #define fi first
    #define se second
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    typedef pair<int,int> pi;
    typedef vector<pi> vc; 
    const int N=5e5+5;
    int ls[N],rs[N],w[N],mn[N],n,l,r,mid,ans;vc f[N];
    inline int min(R int x,R int y){return x<y?x:y;}
    inline void swap(R int &x,R int &y){R int t=x;x=y,y=t;}
    inline bool cmpx(const pi &x,const pi &y){return x.fi<y.fi;}
    inline bool cmpy(const pi &x,const pi &y){return x.se<y.se;}
    void merge(vc &a,vc &ls,vc &rs){
    	R int n=ls.size(),m=rs.size();
    	if(!n||!m)return;
    	if(n>m)ls.swap(rs),swap(n,m);
    	sort(ls.begin(),ls.end(),cmpy);
    	sort(rs.begin(),rs.end(),cmpx);
    	mn[0]=rs[0].se;fp(i,1,m-1)mn[i]=min(mn[i-1],rs[i].se);
    	for(R int i=0,j=m-1;i<n;++i){
    		while(j>=0&&ls[i].se+rs[j].fi>mid)--j;
    		if(j<0)break;a.pb(pi(ls[i].fi,mn[j]));
    	}
    	sort(ls.begin(),ls.end(),cmpx);
    	sort(rs.begin(),rs.end(),cmpy);
    	mn[0]=rs[0].fi;fp(i,1,m-1)mn[i]=min(mn[i-1],rs[i].fi);
    	for(R int i=0,j=m-1;i<n;++i){
    		while(j>=0&&ls[i].fi+rs[j].se>mid)--j;
    		if(j<0)break;a.pb(pi(mn[j],ls[i].se));
    	}
    }
    void dfs(int u){
    	f[u].clear();if(!ls[u])return f[u].pb(pi(w[u],w[u])),void();
    	dfs(ls[u]),dfs(rs[u]);
    	merge(f[u],f[ls[u]],f[rs[u]]);
    	for(auto &v:f[u])v.fi+=w[u],v.se+=w[u];
    }
    int main(){
    	scanf("%d",&n);
    	for(R int i=2,fa,x;i<=n;++i){
    		scanf("%d%d",&fa,&x);
    		(ls[fa]?rs[fa]:ls[fa])=i,w[i]=x;
    	}
    	l=0,r=1e9,ans=1e9;
    	while(l<=r){
    		mid=(l+r)>>1,dfs(1);
    		f[1].empty()?l=mid+1:(ans=mid,r=mid-1);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    (F)

    好迷……

    首先我们发现,一个字母往下的轨迹视为一条路径,那么这条路径会是一条折线,且这个折线越早拐弯越好

    那么我们对于(t)从后往前贪心的匹配,找到(s)中最后面的能匹配的位置,然后判断是否会和之前的折线有交点

    感觉我还是说不太清楚……建议看一下官方题解的图

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=1e6+5;
    char a[N],b[N];int q[N],h,t,n,res;
    int main(){
    	scanf("%d%s%s",&n,a+1,b+1);
    	if(!strcmp(a+1,b+1))return puts("0"),0;
    	h=1,t=0;
    	for(R int i=n,p=n;i;--i)if(b[i]!=b[i-1]){
    		cmin(p,i);while(p&&a[p]!=b[i])--p;
    		if(!p)return puts("-1"),0;
    		while(h<=t&&q[h]-(t-h+1)>=i)++h;
    		q[++t]=p;
    		if(i!=p)cmax(res,t-h+1);
    	}
    	printf("%d
    ",res+1);
    	return 0;
    }
    
  • 相关阅读:
    视频解析小技巧
    linux系统路由设置
    tracert路由跟踪命令
    php+nginx
    docker快速拉取镜像
    linux命令
    添加docker命令
    linux模糊查询文件名
    查看日志
    模板函数与模板类
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11438531.html
Copyright © 2011-2022 走看看