2017-2018 ACM-ICPC, Central Europe Regional Contest (CERC 17)

A. Assignment Algorithm

按题意模拟即可。

#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 = 60, 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;

int n, m, lft, rgt;
int empty[N];
char s[N][20];
int rk[10][2];

int main()
{
	scanf("%d%d", &n, &m);
	lft = n * 4; rgt = n * 4;
	for(int i = 1; i <= n + 3; i ++){
		empty[i] = 9;
		scanf("%s", s[i] + 1);
		for(int j = 1; j <= 11; j ++){
			if(s[i][j] == '#'){
				empty[i] --;
				if(j <= 5) lft --;
				else if(j >= 7) rgt --;
			}
		}
	}
	for(int ch = 0; ch < m; ch ++){
            //printf("%d %d %d\n", ch, lft, rgt);
		int tmpe = 0, o = 0, dis = 100;
		if(ch == 13){
            int go = 1;
		}
		if(empty[2]){
			tmpe = empty[2];
			o = 2;
		}
		if(empty[n / 2 + 3] > tmpe){
			tmpe = empty[n / 2 + 3];
			o = n / 2 + 3;
		}
		if(o == 0){
			for(int i = 3; i <= n / 2 + 1; i ++){
				if(empty[i] > tmpe){
					tmpe = empty[i];
					o = i;
					dis = min(abs(i - n - 3), min(abs(i - 1), abs(i - n / 2 - 2)));
				}
				else if(empty[i] == tmpe){
					int tmpdis = min(abs(i - n - 3), min(abs(i - 1), abs(i - n / 2 - 2)));
					if(tmpdis < dis){
						dis = tmpdis;
						o = i;
					}
				}
			}
			for(int i = n / 2 + 4; i <= n + 2; i ++){
				if(empty[i] > tmpe){
					tmpe = empty[i];
					o = i;
					dis = min(abs(i - n - 3), min(abs(i - 1), abs(i - n / 2 - 2)));
				}
				else if(empty[i] == tmpe){
					int tmpdis = min(abs(i - n - 3), min(abs(i - 1), abs(i - n / 2 - 2)));
					if(tmpdis < dis){
						dis = tmpdis;
						o = i;
					}
				}
			}
		}
		empty[o] --;

		rk[0][0] = 5, rk[0][1] = 7;
		rk[1][0] = 3, rk[1][1] = 9;
		rk[2][0] = 1, rk[2][1] = 11;
		rk[3][0] = 6, rk[3][1] = 6;
		rk[4][0] = 2, rk[4][1] = 10;

		for(int i = 0; i <= 4; i ++){
			if(s[o][rk[i][0]] == '-' || s[o][rk[i][1]] == '-'){
				if(i == 3){
                    s[o][6] = ch + 'a';
                    break;
				}
				if(s[o][rk[i][0]] == '-' && s[o][rk[i][1]] == '-'){
					if(lft >= rgt) s[o][rk[i][0]] = ch + 'a', lft --;
					else s[o][rk[i][1]] = ch + 'a', rgt --;
				}
				else if(s[o][rk[i][0]] == '-'){
					s[o][rk[i][0]] = ch + 'a', lft --;
				}
				else {s[o][rk[i][1]] = ch + 'a', rgt --;}
				break;
			}
		}
	}

	for(int i = 1; i <= n + 3; i ++){
		printf("%s\n", s[i] + 1);
	}
	return 0;
}

/*
2 17
...........
---.#--.---
...........
---.---.---
...........


6 26
...........
---.---.###
#-#.---.---
---.###.---
...........
---.###.---
#--.#-#.--#
#--.--#.#-#
...........



0 17 12
1 16 12
2 15 12
3 15 11
4 15 10
5 14 10
6 13 10
7 12 10
8 12 9
9 12 9
10 11 9
11 10 9
12 10 8
13 9 8
14 8 8
15 8 7
16 8 6
17 7 6
18 7 5
19 6 5
20 5 5
21 4 5
22 4 4
23 4 3
24 4 2
25 4 2
...........
gke.aic.###
#-#.mzo.r-v
t-n.###.p-x
...........
fjb.###.dlh
#-s.#-#.w-#
#-u.qy#.#-#
...........


*/

/*
【trick&&吐槽】


【题意】


【分析】


【时间复杂度&&优化】


*/

  

B. Buffalo Barricades

首先通过扫描线求出最终的图中每个点属于哪个区域,以及包含每个区域的最小区域。

然后倒着处理每个询问,依次删掉每个栅栏,也就是将区域的点数合并,并查集维护。

时间复杂度$O((n+m)\log(n+m))$。

#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
typedef pair<int,int>P;
const int N=600010;
int n,m,ce,i,x,y,z,v[N],f[N],g[N],ans[N];set<P>T;
struct E{int x,y,t;E(){}E(int _x,int _y,int _t){x=_x,y=_y,t=_t;}}e[N];
inline bool cmp(const E&a,const E&b){
  if(a.y!=b.y)return a.y>b.y;
  return a.x<b.x;
}
inline int F(int x){return f[x]==x?x:f[x]=F(f[x]);}
inline void merge(int x,int y){
  x=F(x),y=F(y);
  if(x==y)return;
  f[x]=y,v[y]+=v[x];
}
int main(){
  scanf("%d",&n);
  for(i=1;i<=n;i++){
    scanf("%d%d",&x,&y);
    x<<=1,y<<=1;
    e[++ce]=E(x,y,0);
  }
  scanf("%d",&m);
  for(i=1;i<=m;i++){
    scanf("%d%d",&x,&y);
    x<<=1,y<<=1;
    x++,y++;
    e[++ce]=E(x,y,i);
    f[i]=i;
  }
  sort(e+1,e+ce+1,cmp);
  for(i=1;i<=ce;i++){
    x=e[i].x,z=e[i].t;
    if(z){
      T.insert(P(x,z));
      set<P>::iterator it=T.find(P(x,z)),k=it;
      k++;
      if(k!=T.end())g[z]=k->second;
      while(1){
        k=T.find(P(x,z));
        if(k==T.begin())break;
        k--;
        if(k->second<z)break;
        T.erase(k);
      }
    }else{
      set<P>::iterator it=T.lower_bound(P(x,0));
      if(it!=T.end())v[it->second]++;
    }
  }
  for(i=m;i;i--){
    ans[i]=v[F(i)];
    if(g[i])merge(i,g[i]);
  }
  for(i=1;i<=m;i++)printf("%d\n",ans[i]);
}

  

C. Cumulative Code

留坑。

 

D. Donut Drone

LCT维护环套树,在根与根的父亲处断开。

对于link操作,若成环则无视。

对于cut操作,删边之后再将环边link即可。

对于查询操作,首先求出与环末端点的LCA,这就是进入环的点,剩下的部分模环长后分两段走即可。

时间复杂度$O(n\log n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2010,M=N*N;
int n,m,a[M],f[M],son[M][2],size[M];
int v[N][N],id[N][N],tot,loc[M][2],O;
int i,j,k,x,y,z;char op[100];
inline bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
inline void up(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1;}
inline void rotate(int x){
	int y=f[x],w=son[y][1]==x;
	son[y][w]=son[x][w^1];
	if(son[x][w^1])f[son[x][w^1]]=y;
	if(f[y]){
		int z=f[y];
		if(son[z][0]==y)son[z][0]=x;
		else if(son[z][1]==y)son[z][1]=x;
	}
	f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
}
inline void splay(int x){
	while(!isroot(x)){
		int y=f[x];
		if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
		rotate(x);
	}
	up(x);
}
inline int access(int x){
	int y=0;
	for(;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);
	return y;
}
inline int lca(int x,int y){
	access(x);
	return access(y);
}
inline int root(int x){
	access(x);
	splay(x);
	while(son[x][0])x=son[x][0];
	splay(x);
	return x;
}
inline void link(int x,int y){
	if(root(x)!=root(y))splay(x),f[x]=y,access(x);
}
inline void cut(int x,int y){
	int u=root(x);
	if(u==x)return;
	access(x);
	splay(x);
	f[son[x][0]]=0;
	son[x][0]=0;
	up(x);
	link(u,a[u]);
}
inline int dis(int x){
	access(x);
	splay(x);
	return size[son[x][0]];
}
inline int goup(int x,int k){
	k=dis(x)-k+1;
	access(x);
	splay(x);
	while(1){
		int t=size[son[x][0]]+1;
		if(k==t)return x;
		if(k<t)x=son[x][0];else k-=t,x=son[x][1];
	}
}
inline int moveup(int x,int k){
	x=goup(x,k);
	splay(x);
	return x;
}
inline int simulate(int x,int k){
	int u=root(x),y=a[u],z=lca(x,y);
	int A=dis(x),B=dis(y),C=dis(z);
	if(A>C){
		int t=min(A-C,k);
		k-=t;
		x=moveup(x,t);
	}
	k%=B+1;
	if(!k)return x;
	int t=min(dis(x),k);
	k-=t;
	x=moveup(x,t);
	if(!k)return x;
	x=y;
	k--;
	if(!k)return x;
	return moveup(x,k);
}
inline int getnxt(int o){
	int x=loc[o][0],y=loc[o][1];
	y++;
	y%=m;
	int mx=-1,w=0;
	for(int i=x-1;i<=x+1;i++){
		int nx=(i%n+n)%n;
		if(v[nx][y]>mx){
			mx=v[nx][y];
			w=id[nx][y];
		}
	}
	return w;
}
inline void check(int x,int y){
	x%=n,y%=m;
	x+=n,y+=m;
	x%=n,y%=m;
	int o=id[x][y];
	int now=getnxt(o);
	if(a[o]!=now){
		cut(o,a[o]);
		a[o]=now;
		link(o,a[o]);
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)for(j=0;j<m;j++){
		scanf("%d",&v[i][j]);
		id[i][j]=++tot;
		loc[tot][0]=i;
		loc[tot][1]=j;
	}
	for(i=1;i<=tot;i++)size[i]=1;
	for(i=1;i<=tot;i++){
		a[i]=getnxt(i);
		link(i,a[i]);
	}
	O=id[0][0];
	int _;
	scanf("%d",&_);
	while(_--){
		scanf("%s",op);
		if(op[0]=='c'){
			scanf("%d%d%d",&x,&y,&z);
			x--,y--;
			v[x][y]=z;
			check(x-1,y-1);
			check(x,y-1);
			check(x+1,y-1);
		}else{
			scanf("%d",&k);
			O=simulate(O,k);
			printf("%d %d\n",loc[O][0]+1,loc[O][1]+1);
		}
	}
}
/*
3 4
10 20 30 40
50 60 70 80
90 93 95 99
3
move 4
change 2 1 100
move 4
*/

  

E. Embedding Enumeration

留坑。

 

F. Faulty Factorial

显然要修改的数与$n$相差不超过$P$,枚举每个数分类讨论即可。

#include<cstdio>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int N=10000010;
ll n;
int P,R,i;
int s[N],inv[N];
inline ll po(ll a,ll b,ll P){
	ll t=1;
	for(;b;b>>=1,a=a*a%P)if(b&1)t=t*a%P;
	return t;
}
struct Num{
	ll a,b;
	Num(){a=1,b=0;}
	Num(ll _a,ll _b){a=_a,b=_b;}
	Num operator*(Num x){return Num(a*x.a%P,b+x.b);}
	Num operator/(Num x){return Num(a*inv[x.a]%P,b-x.b);}
}res;
Num cal(ll n){
	return n?Num(s[n%P]*po(s[P],n/P,P)%P,n/P)*cal(n/P):Num(1,0);
}
inline Num ask(ll n){
	ll b=0;
	while(n%P==0)n/=P,b++;
	return Num(n%P,b);
}
void ok(ll a,ll b){
	printf("%lld %lld",a,b);
	exit(0);
}
int main(){
	scanf("%lld%d%d",&n,&P,&R);
	for(i=s[0]=1;i<P;i++)s[i]=1LL*s[i-1]*i%P;
	s[P]=s[P-1];
	for(inv[0]=inv[1]=1,i=2;i<P;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P;
	res=cal(n);
	ll W=1LL*R*inv[res.a]%P;
	for(ll x=n;x>1&&x>n-P-10;x--){
		ll A=x,B=0;
		while(A%P==0)A/=P,B++;
		if(res.b>B){
			if(R)continue;
			ok(x,1);
		}else{
			if(R){
				ll y=A%P*W%P;
				if(y<x)ok(x,y);
			}else{
				if(P<x)ok(x,P);
			}
		}
	}
	puts("-1 -1");
}

  

G. Gambling Guide

设$e_x$表示$x$到$n$的最优期望次数,则$e_n=0,e_x=\frac{\sum\min(e_x,e_y)}{deg_x}+1$。

每次用堆取出$e_x$最小的$x$,更新周围一圈点即可。

时间复杂度$O(m\log m)$。

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef pair<double,int>P;
const int N=300010;
const double inf=1e100;
int n,m,i,x,y,g[N],v[N<<1],nxt[N<<1],ed;
double f[N],s[N];
int d[N],c[N];
bool vis[N];
priority_queue<P,vector<P>,greater<P> >q;
inline void add(int x,int y){
	v[++ed]=y;nxt[ed]=g[x];g[x]=ed;
	d[x]++;
}
inline void ext(int x){
	double now=(s[x]+d[x])/c[x];
	if(now<f[x]){
		f[x]=now;
		q.push(P(now,x));
	}
}
inline void up(int x,double y){
	vis[x]=1;
	for(int i=g[x];i;i=nxt[i]){
		int u=v[i];
		if(vis[u])continue;
		c[u]++;
		s[u]+=y;
		ext(u);
	}
}
int main(){
	scanf("%d%d",&n,&m);
	while(m--){
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	for(i=1;i<n;i++)f[i]=inf;
	q.push(P(0,n));
	while(!q.empty()){
		P t=q.top();q.pop();
		if(vis[t.second])continue;
		up(t.second,t.first);
	}
	printf("%.10f",f[1]);
}

  

H. Hidden Hierarchy

按题意模拟即可。

#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() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#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 L = 1e5 + 10, N = 0, 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;
int T;
char s[L];
map<string, set<string> >mop;
map<string, int>v;
void dfs1(string x)
{
    for(auto y : mop[x])
    {
        dfs1(y);
        v[x] += v[y];
    }
}
void dfs2(string x)
{
    if(!mop[x].size())
    {
        printf("%c %s %d\n", ' ', x.c_str(), v[x]);
        return;
    }
    bool flag = 0;
    for(auto y : mop[x])
    {
        if(v[y] >= T)flag = 1;
    }
    if(!flag)
    {
        printf("%c %s %d\n", '+', x.c_str(), v[x]);
        return;
    }
    else
    {
        printf("%c %s %d\n", '-', x.c_str(), v[x]);
        for(auto y : mop[x])
        {
            dfs2(y);
        }
    }
}
int main()
{
    int n;
while(~scanf("%d", &n))
{
    mop.clear();v.clear();
    for(int i = 1; i <= n; ++i)
    {
        int val;
        scanf("%s%d", s, &val);
        string fa = "#";
        for(int i = 0; s[i]; ++i)if(s[i] == '/')
        {
            char tmp = s[i + 1];
            s[i + 1] = 0;
            mop[fa].insert(s);
            fa = s;
            s[i + 1] = tmp;
        }
        v[fa] += val;
    }
    string rt = "/";
    dfs1(rt);
    //
    //printf("%d\n", v[rt]);
    //
    scanf("%d", &T);
    dfs2(rt);
}
	return 0;
}
/*
【trick&&吐槽】
2
/a/a/a            100
/b.txt            99
200


【题意】


【分析】


【时间复杂度&&优化】


*/

  

I. Intrinsic Interval

若一个区间$[l,r]$满足$max-min=r-l$,则值域连续。

也就是$max-min-r+l=0$,注意到$max-min-r+l\geq 0$恒成立,故为了在合法的情况下区间长度最短,应该满足$v=(max-min-r+l)n+r-l$最小,区间长度即为$v\bmod n$。

利用单调栈求出每个数作为最值的范围,对应$v$的矩形加。

从左往右考虑每个$l$,用线段树维护每个$r$的$v$,对于一个询问,答案即为区间历史最小值,线段树维护即可。

时间复杂度$O((n+m)\log n)$。

#include<cstdio>
#include<algorithm>
#define lc x<<1
#define rc x<<1|1
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int N=100010,M=262150,E=500010;
const ll inf=1LL<<60;
int n,Q,i,j,a[N],t,q[N],l[N],r[N];
int ga[N],gd[N],gq[N],ed,vl[E],vr[E],nxt[E];ll w[E];
P ans[N];
ll m[M],hm[M],d[M],hd[M];
ll fin;
int pos;
inline void Min(ll&a,ll b){a>b?(a=b):0;}
inline void hdoa(int x,ll v){
	Min(hm[x],m[x]+v);
	Min(hd[x],d[x]+v);
}
inline void doa(int x,ll v){
	Min(hm[x],m[x]+=v);
	Min(hd[x],d[x]+=v);
}
inline void pb(int x){
	if(hd[x])hdoa(lc,hd[x]),hdoa(rc,hd[x]),hd[x]=0;
	if(d[x])doa(lc,d[x]),doa(rc,d[x]),d[x]=0;
}
inline void up(int x){
	m[x]=min(m[lc],m[rc]);
	Min(hm[x],min(hm[lc],hm[rc]));
}
void build(int x,int a,int b){
	if(a==b){
		hm[x]=m[x]=-1LL*a*(n-1);
		return;
	}
	int mid=(a+b)>>1;
	build(x<<1,a,mid);
	build(x<<1|1,mid+1,b);
	up(x);
}
void dfs(int x,int a,int b){
	if(a==b){
		hm[x]=m[x];
		return;
	}
	pb(x);
	int mid=(a+b)>>1;
	dfs(x<<1,a,mid);
	dfs(x<<1|1,mid+1,b);
	m[x]=min(m[lc],m[rc]);
	hm[x]=min(hm[lc],hm[rc]);
}
void add(int x,int f,int t,int qf,int qt,ll v){
	if(qf<=f&&t<=qt){doa(x,v);return;}
	pb(x);
	int mid=(f+t)>>1;
	if(qf<=mid)add(lc,f,mid,qf,qt,v);
	if(qt>mid)add(rc,mid+1,t,qf,qt,v);
	up(x);
}

inline void addedge(int&x,int l,int r,ll z){
	vl[++ed]=l;
	vr[ed]=r;
	w[ed]=z;
	nxt[ed]=x;
	x=ed;
}
inline void ext(int xl,int xr,int yl,int yr,ll w){
	//printf("%d %d %d %d %lld\n",xl,xr,yl,yr,w);
	if(w>=0){
		addedge(ga[xl],yl,yr,w);
		addedge(gd[xr+1],yl,yr,-w);
	}else{
		addedge(gd[xl],yl,yr,w);
		addedge(ga[xr+1],yl,yr,-w);
	}
}

void ask(int x,int a,int b,int c,int d){
	if(c<=a&&b<=d){
		Min(fin,hm[x]);
		return;
	}
	pb(x);
	int mid=(a+b)>>1;
	if(c<=mid)ask(x<<1,a,mid,c,d);
	if(d>mid)ask(x<<1|1,mid+1,b,c,d);
}
void getpos(int x,int a,int b,int c,int d){
	if(pos)return;
	if(hm[x]>fin)return;
	if(a==b){
		pos=a;
		return;
	}
	pb(x);
	int mid=(a+b)>>1;
	if(c<=mid)getpos(x<<1,a,mid,c,d);
	if(d>mid)getpos(x<<1|1,mid+1,b,c,d);
}

inline P query(int l,int r){
	fin=inf;
	ask(1,1,n,r,n);
	//printf("! %d %d %lld\n",l,r,fin);
	pos=0;
	getpos(1,1,n,r,n);
	return P(pos-fin%n,pos);
}

int main(){
	//(ma-mi-r+l)*n+r-l
	//ma*n-mi*n-r*(n-1)+l*(n-1)
	scanf("%d",&n);
	for(i=1;i<=n;i++)scanf("%d",&a[i]);
	scanf("%d",&Q);
	for(i=1;i<=Q;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		addedge(gq[x],i,y,0);
	}
	
	for(q[t=0]=0,i=1;i<=n;i++){
		while(t&&a[i]>a[q[t]])t--;
		l[i]=q[t]+1;
		q[++t]=i;
	}
	for(q[t=0]=n+1,i=n;i;i--){
		while(t&&a[i]>a[q[t]])t--;
		r[i]=q[t]-1;
		q[++t]=i;
	}
	for(i=1;i<=n;i++)ext(l[i],i,i,r[i],1LL*a[i]*n);
	
	for(q[t=0]=0,i=1;i<=n;i++){
		while(t&&a[i]<a[q[t]])t--;
		l[i]=q[t]+1;
		q[++t]=i;
	}
	for(q[t=0]=n+1,i=n;i;i--){
		while(t&&a[i]<a[q[t]])t--;
		r[i]=q[t]-1;
		q[++t]=i;
	}
	for(i=1;i<=n;i++)ext(l[i],i,i,r[i],-1LL*a[i]*n);
	
	build(1,1,n);
	for(i=1;i<=n;i++){
		add(1,1,n,1,n,n-1);
		for(j=ga[i];j;j=nxt[j]){
			add(1,1,n,vl[j],vr[j],w[j]);
		}
		for(j=gd[i];j;j=nxt[j]){
			add(1,1,n,vl[j],vr[j],w[j]);
		}
		if(i==1)dfs(1,1,n);
		for(j=gq[i];j;j=nxt[j]){
			ans[vl[j]]=query(i,vr[j]);
		}
	}
	
	for(i=1;i<=Q;i++)printf("%d %d\n",ans[i].first,ans[i].second);
}
/*
7
3 1 7 5 6 4 2
3
3 6
7 7
1 3


7
3 1 7 5 6 4 2
3
3 6
7 7
1 3
1 1 1 2 21
2 2 2 2 7
1 3 3 7 49
4 4 4 4 35
4 5 5 7 42
6 6 6 7 28
7 7 7 7 14
1 1 1 1 -21
1 2 2 7 -7
3 3 3 3 -49
3 4 4 5 -35
5 5 5 5 -42
3 6 6 6 -28
3 7 7 7 -14
! 1 3 -12
! 3 6 -30
! 7 7 -36
8 6
8 7
8 3

*/

  

J. Justified Jungle

枚举每个$k$,那么剩下每个子树大小均为$\frac{n}{k+1}$,因此$k+1$一定是$n$的因子,若此时还满足子树大小为它的倍数的点数够$k+1$个,则可行。

#include<cstdio>
const int N=1000010;
int n,i,x,y,g[N],v[N<<1],nxt[N<<1],ed;
int size[N];
int cnt[N];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x,int y){
	size[x]=1;
	for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x),size[x]+=size[v[i]];
	cnt[size[x]]++;
}
inline bool check(int x){
	x++;
	if(n%x)return 0;
	int w=n/x,t=0;
	for(int i=w;i<=n;i+=w)t+=cnt[i];
	return t==x;
}
int main(){
	scanf("%d",&n);
	for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
	dfs(1,0);
	for(i=1;i<n;i++)if(check(i))printf("%d ",i);
}

  

K. Kitchen Knobs

因为$7$的因子只有$1$和$7$,因此最大值要么有$1$种,要么有$7$种,$7$种的显然无论怎么操作都是最优的,可以直接删掉。

问题转化为:给定$n$个数$a_1,a_2,...,a_n$,每次可以选择一个区间加上一个数,用最少的操作次数让所有数模$7$都为$0$。

将$a$差分,区间加操作也差分,则问题转化为:每次可以选择两个数,一个加上$k$,另一个减去$k$,用最少的操作次数让所有数模$7$都为$0$。

首先无视$0$,那么一定是先$1,6$配对、$2,5$配对、$3,4$配对,如此处理后最多还有$3$种数字$A,B,C$,问题转化为将这些数分成最多的组数,使得每组和都为$0$,记录$3$个数每个的个数然后DP即可。

#include<cstdio>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=505,inf=10000;
int _,n,a[N],A,B,C,i,j,k,x,y,z,cnt[7],ans,m,e[N][4];short f[N][N][N],t;
inline void up(short&a,short b){a>b?(a=b):0;}
int ask(){
  static char s[20];
  scanf("%s",s);
  string best="";
  for(int i=0;i<7;i++)best.push_back(s[i]);
  int mask=1;
  int sum=0;
  for(int i=1;i<7;i++){
    string now="";
    for(int j=0;j<7;j++){
      now.push_back(s[(i+j)%7]);
    }
    if(now==best){
      mask|=1<<i;
      sum+=i;
    }else if(now>best){
      mask=1<<i;
      sum=i;
      best=now;
    }
  }
  if(mask!=(mask&(-mask)))return -1;
  return sum;
}
int main(){
  scanf("%d",&_);
  while(_--){
    int x=ask();
    if(x<0)continue;
    a[++n]=x;
  }
  if(!n)return puts("0"),0;
  for(i=n+1;i;i--)a[i]-=a[i-1];
  for(i=1;i<=n+1;i++)a[i]=((a[i]%7+7)%7);
  for(i=1;i<=n+1;i++)cnt[a[i]]++;
  while(cnt[1]&&cnt[6])cnt[1]--,cnt[6]--,ans++;
  A=cnt[1]?cnt[1]:cnt[6];
  x=cnt[1]?1:6;
  while(cnt[2]&&cnt[5])cnt[2]--,cnt[5]--,ans++;
  B=cnt[2]?cnt[2]:cnt[5];
  y=cnt[2]?2:5;
  while(cnt[3]&&cnt[4])cnt[3]--,cnt[4]--,ans++;
  C=cnt[3]?cnt[3]:cnt[4];
  z=cnt[3]?3:4;
  for(i=0;i<=7;i++)for(j=0;j<=7;j++)for(k=0;k<=7;k++)if(i+j+k&&(i*x+j*y+k*z)%7==0){
    e[m][0]=i;
    e[m][1]=j;
    e[m][2]=k;
    e[m++][3]=i+j+k-1;
  }
  for(i=0;i<=A;i++)for(j=0;j<=B;j++)for(k=0;k<=C;k++)if(i+j+k){
    t=inf;
    for(x=0;x<m;x++)if(i>=e[x][0]&&j>=e[x][1]&&k>=e[x][2])up(t,f[i-e[x][0]][j-e[x][1]][k-e[x][2]]+e[x][3]);
    f[i][j][k]=t;
  }
  printf("%d",ans+f[A][B][C]);
}

  

L. Lunar Landscape

利用二维前缀和标记出所有被覆盖的位置即可。

#include<cstdio>
const int K=2005,N=4100;
int n,x,y,d,i,j,k,a[N][N],b[N][N],f[N][N],ans;char op[9];
int main(){
  scanf("%d",&n);
  while(n--){
    scanf("%s%d%d%d",op,&x,&y,&d);
    x+=K,y+=K;
    if(op[0]=='A'){
      x-=d/2,y-=d/2;
      a[x][y]++;
      a[x+d][y]--;
      a[x][y+d]--;
      a[x+d][y+d]++;
    }else{
      d/=2;
      b[x][y-d]++;
      b[x-d][y]--;
      b[x+d][y]--;
      b[x][y+d]++;
    }
  }
  for(i=1;i<N;i++)for(j=1;j<N;j++){
    a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
    if(a[i][j])f[i][j]|=15;
  }
  for(j=1;j<N;j++)for(i=1;i<N;i++){
    b[i][j]+=b[i-1][j-1]+b[i+1][j-1];
    if(j>=2)b[i][j]-=b[i][j-2];
    if(b[i][j]){
      f[i][j]|=12;
      f[i][j+1]|=9;
      f[i-1][j]|=6;
      f[i-1][j+1]|=3;
    }
  }
  for(i=1;i<N;i++)for(j=1;j<N;j++)for(k=0;k<4;k++)if(f[i][j]>>k&1)ans++;
  printf("%.2f",0.25*ans);
}

  

posted @ 2017-12-15 03:51 Claris 阅读(...) 评论(...) 编辑 收藏