2017-2018 Northwestern European Regional Contest (NWERC 2017)

A. Ascending Photo

留坑。

 

B. Boss Battle

当$n\leq 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\n",co);
		printf("%lld incorrect\n",tot-co);
	}
}

  

E. English Restaurant

留坑。

 

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\n",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]\n",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\n", 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\n", 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\n", 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\n",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(n\log 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\n",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);
	}
}

  

posted @ 2017-12-01 02:21 Claris 阅读(...) 评论(...) 编辑 收藏