• 2017-2018 Northwestern European Regional Contest (NWERC 2017)


    A. Ascending Photo

    贪心增广。

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN = 1000000 + 10;
    vector<int> g[MAXN];
    int a[MAXN], b[MAXN], sz[MAXN], cnt[MAXN];
    bool mg[MAXN], vis[MAXN];
    int n, m;
    bool dfs(int u, int f = -1) {
      if (g[u].empty()) return false;
      for (auto &p: g[u]) if (p != f) {
        if (!vis[p + 1] || cnt[u + 1] == 1) {
          mg[p] = vis[p] = vis[p + 1] = 1;
          if (f != -1) mg[f] = 0;
          return true;
        }
      }
      for (auto &p: g[u]) if (p != f) {
        if (dfs(u + 1, p + 1)) {
          mg[p] = vis[p] = vis[p + 1] = 1;
          if (f != -1) mg[f] = 0;
          return true;
        }
      }
      return false;
    }
    int main(){
      scanf("%d", &n); m = 0;
      for (int i = 0, p(-1); i < n; ++ i) {
        int x; scanf("%d", &x);
        if (x == p) sz[m - 1] ++;
        else {
          p = x; a[m] = x;
          sz[m ++] = 1;
        }
      }
      n=m;
      for(int i=0;i<n;i++)b[i]=a[i];
      sort(b,b+n);
      for(int i=m=0;i<n;i++)if(i==0||b[i]>b[m-1])b[m++]=b[i];
      for (int i = 0; i < n; ++ i) {
        a[i] = lower_bound(b,b+m,a[i])-b;
        cnt[a[i]] ++;
      }
      for (int i = 0; i + 1 < n; ++ i) {
        if (a[i] + 1 == a[i + 1]) g[a[i]].push_back(i);
      }
      int ret = n;
      for (int i = m - 1; i >= 0; -- i) {
        if (dfs(i)) -- ret;
      }
      printf("%d", ret);
    }
    

      

    B. Boss Battle

    当$nleq 3$时显然$1$步就可以炸死。否则每次可以缩小一格,故答案为$n-2$。

    #include<cstdio>
    int n,ans;
    int main(){
    	scanf("%d",&n);
    	if(n<=3)ans=1;
    	else ans=n-2;
    	printf("%d",ans);
    }
    

      

    C. Connect the Dots

    留坑。

    D. Dunglish

    按题意模拟即可。

    #include<cstdio>
    #include<iostream>
    #include<map>
    #include<string>
    using namespace std;
    int n,m,i;
    string a[100];
    long long tot,co;
    map<string,int>f,g;
    map<string,string>o;
    int main(){
    	cin>>n;
    	for(i=1;i<=n;i++){
    		cin>>a[i];
    	}
    	cin>>m;
    	while(m--){
    		string a,b,c;
    		cin>>a>>b>>c;
    		o[a]=b;
    		if(c[0]=='c')f[a]++;else g[a]++;
    	}
    	tot=1;
    	co=1;
    	for(i=1;i<=n;i++){
    		tot*=f[a[i]]+g[a[i]];
    		co*=f[a[i]];
    	}
    	if(tot==1){
    		for(i=1;i<=n;i++)cout<<o[a[i]]<<" "<<endl;
    		if(co==1){
    			printf("correct");
    		}else{
    			printf("incorrect");
    		}
    	}else{
    		printf("%lld correct
    ",co);
    		printf("%lld incorrect
    ",tot-co);
    	}
    }
    

      

    E. English Restaurant

    在最后新添$n$个容量$+infty$的桌子,表示离开餐馆,然后将桌子按容量排序。

    因为$期望=frac{总和}{方案数}$,所以可以同时DP出总和以及方案数。

    注意到最终占据的一定是若干个区间,首先预处理出$w[i][j]$表示只有$[i,j]$桌子被占据的总和以及方案数,枚举最后一张桌子转移。

    然后设$f[i][j]$表示$[i,n]$这些人占据了$[j,t]$这些桌子的总和以及方案数,利用前缀和优化转移。

    转移时需要用组合数体现顺序,时间复杂度$O(n^3)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=210;
    int n,g,t,i,j,k,x,y,c[N];double C[N][N];
    struct P{
      double u,d;
      P(){}
      P(double _u,double _d){u=_u,d=_d;}
      P operator+(const P&b){return P(u*b.d+d*b.u,d*b.d);}
      P operator*(const P&b){return P(u+b.u,d+b.d);}
      void operator+=(const P&b){*this=*this+b;}
      void operator*=(const P&b){*this=*this*b;}
    }w[N][N],f[N][N],s[N][N],tmp;
    inline P cal(int l,int r){
      int x=min(g,l>0?c[l-1]:0),y=min(g,c[r]);
      return P(c[r]<N?(y*(y+1)-x*(x+1))/2:0,y-x);
    }
    int main(){
      scanf("%d%d%d",&n,&g,&t);
      for(i=0;i<n;i++)scanf("%d",&c[i]);
      sort(c,c+n);
      for(i=0;i<t;i++)c[n++]=N;
      for(C[0][0]=1,i=1;i<n+5;i++)for(C[i][0]=1,j=1;j<=i;j++)C[i][j]=C[i-1][j-1]+C[i-1][j];
      for(j=0;j<n;j++){
        w[j][j]=cal(j,j);
        for(i=j-1;~i;i--)for(k=i;k<=j;k++){
          tmp=P(0,C[j-i][k-i])+cal(i,k);
          if(i<k)tmp+=w[i][k-1];
          if(j>k)tmp+=w[k+1][j];
          w[i][j]*=tmp;
        }
      }
      for(i=t-1;~i;i--)for(j=n-1;~j;j--)if(t-i<=n-j)f[i][j]=w[j][j+t-i-1];
      for(i=t-1;~i;i--)for(j=n-1;~j;j--){
        for(x=i+1;x<t;x++)f[i][j]*=P(0,C[t-i][t-x])+w[j][j+x-i-1]+s[x][j+x-i+1];
        s[i][j]=s[i][j+1]*f[i][j];
      }
      tmp=P(0,0);
      for(i=0;i<n;i++)tmp*=f[0][i];
      return printf("%.15f",tmp.u/tmp.d),0;
    }
    

      

    F. Factor-Free Tree

    限制条件等价于每个点和子树内每个点都互质,这说明在中序遍历上往前往后若干个互质。

    分解质因数,维护每个质因子最后一次出现的位置,即可求出$[l_i,r_i]$表示$i$与往前往后多少范围内都互质。

    按中序遍历从左往右依次考虑每个点,用一个栈维护之前部分的树的最右链,对于$i$,先将它往下挂,然后尝试往上浮动,因为$r$越大的点越靠近根更优,除非$l$太大以至于不能覆盖住左边的子树。

    一旦一个点的父亲确定,那么它的超过父亲的$r$的部分是毫无用处的,将$r$直接和父亲取$min$。

    如此一来,每条链上满足$r$递减,故直接暴力退栈直到找到合适的位置即可。

    构造部分时间复杂度为$O(n)$。

    #include<stdio.h>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() {  }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 1000010, M = 10000010, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    int n,i,j;
    int v[M],p[N],tot,last[M];
    int a[N],left[N],right[N];
    struct S
    {
    	int l, r, rpow;
    }s[N];
    int fa[N];
    int main()
    {
    	for(i=2;i<M;i++){
    		if(!v[i]){
    			p[tot++]=i;
    			v[i]=i;
    		}
    		for(j=0;j<tot&&i*p[j]<M;j++){
    			v[i*p[j]]=p[j];
    			if(i%p[j]==0)break;
    		}
    	}
    	while(~scanf("%d",&n))
    	{
    		for(i=1;i<=n;i++)scanf("%d",&a[i]);
    		for(i=2;i<M;i++)last[i]=0;
    		for(i=1;i<=n;i++){
    			left[i]=0;
    			int x=a[i];
    			while(x>1){
    				//printf("! %d %d
    ",i,v[x]);
    				left[i]=max(left[i],last[v[x]]);
    				x/=v[x];
    			}
    			left[i]++;
    			x=a[i];
    			while(x>1){
    				last[v[x]]=i;
    				x/=v[x];
    			}
    		}
    		for(i=2;i<M;i++)last[i]=n+1;
    		for(i=n;i;i--){
    			right[i]=n+1;
    			int x=a[i];
    			while(x>1){
    				right[i]=min(right[i],last[v[x]]);
    				x/=v[x];
    			}
    			right[i]--;
    			x=a[i];
    			while(x>1){
    				last[v[x]]=i;
    				x/=v[x];
    			}
    		}
    		//for(i=1;i<=n;i++)printf("%d [%d,%d]
    ",i,left[i],right[i]);
    
    		int top = 0;
    		s[0].rpow = inf;
    		bool flag = 1;
    		for(int i = 1; i <= n; ++i)
    		{
    			fa[i] = 0;
    			s[top + 1].r = 0;
    			int lft = i;
    			while(left[i] <= s[top].l && s[top].rpow <= right[i])
    			{
    				gmin(lft, s[top].l);
    				--top;
    			}
    			fa[s[top + 1].r] = i;
    			fa[i] = s[top].r;
    			++top;
    			s[top].l = lft;
    			s[top].r = i;
    			s[top].rpow = min(right[i], s[top - 1].rpow);
    			//printf("i: %d l = %d r = %d rpow = %d
    ", i, s[top].l, s[top].r, s[top].rpow);
    			if(s[top - 1].rpow < i)
    			{
    				flag = 0;
    				//printf("i = %d s[top - 1].rpow = %d
    ", i, s[top - 1].rpow);
    			}
    		}
    		if(!flag)puts("impossible");
    		else
    		{
    			for(int i = 1; i <= n; ++i)printf("%d ", fa[i]);
    			puts("");
    		}
    	}
    	return 0;
    }
    
    /*
    【trick&&吐槽】
    
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

    G. Glyph Recognition

    枚举形状,然后二分求出半径。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() {  }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 1010, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    const double eps = 1e-8;
    inline int sgn(double x) {return fabs(x) < eps ? 0 : (x < 0 ? -1 : 1);}
    inline double sqr(double x){return x * x;}
    
    struct point
    {
        double x, y;
        point(){}
        point(double a, double b) : x(a), y(b) {}
        friend point operator + (const point &a, const point &b){
            return point(a.x + b.x, a.y + b.y);
        }
        friend point operator - (const point &a, const point &b){
            return point(a.x - b.x, a.y - b.y);
        }
        friend point operator * (const point &a, const double &b){
            return point(a.x * b, a.y * b);
        }
        friend point operator * (const double &a, const point &b){
            return point(a * b.x, a * b.y);
        }
        friend point operator / (const point &a, const double &b){
            return point(a.x / b, a.y / b);
        }
        double norm(){
            return sqrt(sqr(x) + sqr(y));
        }
    } ;
    
    double det(const point &a, const point &b)
    {
        return a.x * b.y - a.y * b.x;
    }
    double dot(const point &a, const point &b)
    {
        return a.x * b.x + a.y * b.y;
    }
    double dist(const point &a, const point &b)
    {
        return (a - b).norm();
    }
    point rotate_point(const point &p, double A)
    {
        double tx = p.x, ty = p.y;
        return point(tx * cos(A) - ty * sin(A), tx * sin(A) + ty * cos(A));
    }
    bool PointOnSegment(point p, point s, point t)
    {
        return sgn(det(p - s, t - s)) == 0 && sgn(dot(p - s, p - t)) <= 0;
    }
    struct polygon
    {
        int n;
        point a[N];
        polygon(){}
        double area(){
            double sum = 0;
            a[n] = a[0];
            for(int i = 0; i < n; i ++) sum += det(a[i + 1], a[i]);
            return sum / 2;
        }
        int Point_In(point t){
            int num = 0, i, d1, d2, k;
            a[n] = a[0];
            for(i = 0; i < n; i ++){
                if(PointOnSegment(t, a[i], a[i + 1])) return 2;
                k = sgn(det(a[i + 1] - a[i], t - a[i]));
                d1 = sgn(a[i].y - t.y);
                d2 = sgn(a[i + 1].y - t.y);
                if(k > 0 && d1 <= 0 && d2 > 0) num ++;
                if(k < 0 && d2 <= 0 && d1 > 0) num --;
            }
            return num != 0;
        }
    }c;
    
    point p[N];
    const double PI = acos(-1.0);
    void make(int n, double r)
    {
        double ang = PI * 2.0 / n;
        c.a[0] = point(r, 0);
        for(int i = 1; i < n; i ++){
            c.a[i] = rotate_point(c.a[0], PI * 2 - ang * i);
        }
    }
    
    int n;
    
    bool check()
    {
        for(int i = 0; i < n; i ++){
            if(c.Point_In(p[i]) == 0) return 0;
        }
        return 1;
    }
    
    bool check2()
    {
        for(int i = 0; i < n; i ++){
            if(c.Point_In(p[i]) == 1) return 0;
        }
        return 1;
    }
    const double INF = 1e9;
    int main()
    {
        scanf("%d", &n);
        for(int i = 0; i < n; i ++){
            scanf("%lf%lf", &p[i].x, &p[i].y);
        }
        double k = 0;
        int ans;
        for(int i = 3; i <= 8; i ++){
            c.n = i;
            double l = 0, r = INF;
            for(int tim = 1; tim <= 1000; tim ++){
                double mid = (l + r) / 2;
                make(i, mid);
                if(check()) r = mid;
                else l = mid;
            }
            double out = c.area();
            l = 0, r = INF;
            for(int tim = 1; tim <= 1000; tim ++){
                double mid = (l + r) / 2;
                make(i, mid);
                if(check2()) l = mid;
                else r = mid;
            }
            double in = c.area();
            double tmp = in / out;
            if(tmp > k){
                k = tmp;
                ans = i;
            }
        }
        printf("%d %.10f
    ", ans, k);
    }
    
    /*
    【trick&&吐槽】
    
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

    H. High Score

    因为平方增长较快,所以全部给某个数是最优的,但在小数据下不一定成立,故小数据暴力枚举即可。

    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int K=1000;
    int T;ll a,b,c,d,i,j,k,ans;
    inline ll cal(ll a,ll b,ll c){return a*a+b*b+c*c+min(a,min(b,c))*7;}
    int main(){
    	cin>>T;
    	while(T--){
    		cin>>a>>b>>c>>d;
    		ans=0;
    		for(i=0;i<=d&&i<=K;i++)for(j=0;i+j<=d&&j<=K;j++){
    			k=d-i-j;
    			ans=max(ans,cal(a+i,b+j,c+k));
    			ans=max(ans,cal(a+i,b+k,c+j));
    			ans=max(ans,cal(a+k,b+i,c+j));
    		}
    		ans=max(ans,cal(a+d,b,c));
    		ans=max(ans,cal(a,b+d,c));
    		ans=max(ans,cal(a,b,c+d));
    		cout<<ans<<endl;
    	}
    }
    

      

    I. Installing Apps

    每个应用可以看成会先占用$max(d,s)$的空间,然后释放$max(d,s)-s$的空间。

    假设全部应用都要安装,那么按照释放空间的大小从大到小安装最优。

    如此排序之后设$f[i][j]$表示考虑前$i$个应用,剩余空间为$j$时最多安装几个应用即可。

    时间复杂度$O(nc)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=505,M=10010;
    int n,m,i,j,x,f[N][M],g[N][M],w[N][M],q[N],cnt;
    struct P{int p,w,t;}a[N];
    inline bool cmp(const P&a,const P&b){return a.t>b.t;}
    int main(){
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++){
    		int d,s;
    		scanf("%d%d",&d,&s);
    		a[i].p=i;
    		a[i].w=max(d,s);
    		a[i].t=a[i].w-s;
    	}
    	sort(a+1,a+n+1,cmp);
    	for(i=0;i<=n;i++)for(j=0;j<=m;j++)f[i][j]=-M;
    	f[0][m]=0;
    	for(i=1;i<=n;i++){
    		for(j=0;j<=m;j++){
    			f[i][j]=f[i-1][j];
    			g[i][j]=j;
    			w[i][j]=0;
    		}
    		for(j=0;j<=m;j++)if(f[i-1][j]>=0){
    			if(j<a[i].w)continue;
    			if(f[i-1][j]+1>f[i][j-a[i].w+a[i].t]){
    				f[i][j-a[i].w+a[i].t]=f[i-1][j]+1;
    				g[i][j-a[i].w+a[i].t]=j;
    				w[i][j-a[i].w+a[i].t]=a[i].p;
    			}
    		}
    	}
    	for(i=x=0;i<=m;i++)if(f[n][i]>f[n][x])x=i;
    	printf("%d
    ",f[n][x]);
    	for(i=n;i;i--){
    		if(w[i][x])q[++cnt]=w[i][x];
    		x=g[i][x];
    	}
    	for(i=cnt;i;i--)printf("%d ",q[i]);
    }
    

      

    J. Juggling Troupe

    对于$i$处的$2$,往前往后找到第一个$0$的位置$l,r$,将$l$和$r$赋值为$1$,然后将$l+r-i$赋值为$0$即可。

    set维护,时间复杂度$O(nlog n)$。

    #include<cstdio>
    #include<set>
    #include<cstring>
    using namespace std;
    const int N=1000010;
    set<int>T;int n,i,x;char a[N];
    int main(){
      scanf("%s",a+1);
      n=strlen(a+1);
      T.insert(0);
      T.insert(n+1);
      for(i=1;i<=n;i++)if(a[i]=='0')T.insert(i);
      for(i=1;i<=n;i++)if(a[i]=='2'){
        set<int>::iterator r=T.lower_bound(i),l=r;
        l--;
        x=*l+*r-i;
        if(*l>0)T.erase(l);
        if(*r<=n)T.erase(r);
        T.insert(x);
      }
      for(i=1;i<=n;i++)a[i]='1';
      for(set<int>::iterator it=T.begin();it!=T.end();it++)a[*it]='0';
      for(i=1;i<=n;i++)putchar(a[i]);
    }
    

      

    K. Knockout Tournament

    对于$1$,要让他的对手尽量弱小,而对于其他人,要让他的对手和他水平尽量接近,以降低他的胜率。

    故将$1$看作$0$水平后从小到大排序,相邻的配对即可。

    然后计算概率的时候只需要暴力枚举两边的胜者,两个点只会在lca处被计算,故时间复杂度为$O(n^2)$。

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    typedef pair<int,double>P;
    const int N=8200;
    int n,m,i,j,a[N],start;
    vector<P>f[N];
    inline double win(int x,int y){
    	return 1.0*a[x]/(a[x]+a[y]);
    }
    inline void cal(int x){
    	int l=x<<1,r=x<<1|1;
    	for(int _=0;_<2;_++){
    		for(vector<P>::iterator i=f[l].begin();i!=f[l].end();i++){
    			int A=i->first;
    			double B=0;
    			for(vector<P>::iterator j=f[r].begin();j!=f[r].end();j++){
    				B+=win(A,j->first)*j->second;
    			}
    			f[x].push_back(P(A,B*i->second));
    		}
    		swap(l,r);
    	}
    }
    int id[N],cnt;
    void dfs(int x){
    	if((x<<1)>m){
    		id[++cnt]=x;
    		//printf("%d %d
    ",cnt,x);
    	}
    	if((x<<1)<=m)dfs(x<<1);
    	if((x<<1|1)<=m)dfs(x<<1|1);
    }
    int main(){
    	scanf("%d",&n);
    	for(i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    	}
    	a[0]=a[1];
    	a[1]=0;
    	sort(a+1,a+n+1);
    	a[1]=a[0];
    	m=n*2-1;
    	start=m-n+1;
    	dfs(1);
    	for(i=1;i<=n;i++){
    		int x=id[n-i+1];
    		f[x].push_back(P(i,1));
    	}
    	for(i=start-1;i;i--){
    		cal(i);
    	}
    	for(i=0;i<f[1].size();i++)if(f[1][i].first==1){
    		printf("%.10f",f[1][i].second);
    	}
    }
    

      

  • 相关阅读:
    网线接线分类
    MongoDB修改用户密码
    win10计算器和商店英文改中文
    电脑微信双开
    ajax
    get和post的区别
    javascript中各种继承方式的优缺点
    原型
    高阶函数的封装
    深浅拷贝
  • 原文地址:https://www.cnblogs.com/clrs97/p/7936483.html
Copyright © 2020-2023  润新知