Codeforces Round #601 (CF1254 A,B,C,D,E)

A - Feeding Chicken

Sol

将方格按照蛇形抽成序列:

网格.png

序列中的连续一段格子必然形成连通块。对这个序列进行划分即可。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=110;
int n,m;
char str[N];
int seq[N*N],col[N*N];
char mp[100];
int main() {
	for(int i=0;i<26;++i) mp[i+1]='a'+i;
	for(int i=0;i<26;++i) mp[i+27]='A'+i;
	for(int i=0;i<10;++i) mp[i+53]='0'+i;
	int T; rd(T);
	while(T--) {
		int k;
		rd(n),rd(m),rd(k);
		int tot=0,pos=0;
		for(int i=1;i<=n;++i) {
			scanf("%s",str+1);
			if(i&1) {
				for(int j=1;j<=m;++j) {
					seq[++pos]=str[j]=='R';
					tot+=str[j]=='R';
				}
			}
			else {
				for(int j=m;j>=1;--j) {
					seq[++pos]=str[j]=='R';
					tot+=str[j]=='R';
				}
			}
		}
		int lst=1,lim;
		for(lim=tot/k,tot-=lim;;--k,lim=tot/k,tot-=lim) {
			for(;lim;lim-=seq[lst],col[lst]=k,lst++);
			if(k==1) break;
		}
		for(;lst<=pos;col[lst]=k,lst++);
		for(int i=1;i<=n;++i,puts("")) {
			if(i&1) {
				for(int j=1;j<=m;++j)
					putchar(mp[col[(i-1)*m+j]]);
			}
			else {
				for(int j=1;j<=m;++j)
					putchar(mp[col[(i-1)*m+(m-j+1)]]);
			}
		}
	}
	return 0;
}

B - Send Boxes to Alice

Sol

k k k必须是 ∑ a i \sum a_i ai的因子。

如果确定了 k k k,策略必然是按照在序列中的位置从左到右把巧克力排开,每连续的 k k k个放到一起。放的位置是这些盒子的位置的中位数。

显然当 k k k不是质数的时候一定不优秀。

枚举 ∑ a i \sum a_i ai的每个质因子算答案取最优即可。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
inline int Abs(int x) { return x>0?x:-x; }
const int N=1e6+10;
ll m,a[N],b[N];
int n;
ll sol(int l,int r,ll pos) {
	pos=(pos+1)/2; ll cur=0;
	int mid;
	for(int i=l;i<=r;++i) {
		cur+=b[i];
		if(cur>=pos) { mid=i; break; }
	}
	ll ans=0;
	for(int i=l;i<=r;++i) ans+=Abs(i-mid)*b[i];
	return ans;
}
ll work(ll pr) {
	ll ans=0;
	int lst=1; ll cnt=0;
	for(int i=1;i<=n;++i) {
		if(cnt+a[i]>=pr) {
			b[i]=pr-cnt;
			ans+=sol(lst,i,pr);
			b[i]=(a[i]-(pr-cnt))%pr;
			cnt=b[i];
			lst=i;
		}
		else cnt+=a[i],b[i]=a[i];
	}
	return ans;
}
int main() {
	rd(n);
	for(int i=1;i<=n;++i) rd(a[i]),m+=a[i];
	if(m==1) {
		printf("-1");
		return 0;
	}
	ll ans=1e18;
	for(int i=2;i*(ll)i<=m;++i) if(m%i==0) {
		while(m%i==0) m/=i;
		ans=min(ans,work(i));
	}
	if(m>1) ans=min(ans,work(m));
	printf("%lld",ans);
	return 0;
}

C - Point Ordering

Sol

首先随便选两个点 p 1 , p 2 p_1,p_2 p1,p2,然后对剩下的点问一遍 2 2 2,区分出它们在 p 1 p 2 → \overrightarrow{p_1p_2} p1p2 划分出的哪个半平面内。

对于每个半平面内:问出所有点与 p 1 , p 2 p_1,p_2 p1,p2组成的三角形的面积,假设这其中面积最大的那个点是 q q q,再问出每个点是在 p 1 q → \overrightarrow{p_1q} p1q 的哪个方向,从 p 1 p_1 p1 q q q之间的那些点按照与 p 1 , p 2 p_1,p_2 p1,p2组成的三角形面积从小到大排列, q q q p 2 p_2 p2之间的点从大到小排列。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define PB push_back 
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
int n,ans;
vector<int> v1,v2;
vector<int> s1,s2;
ll ar[1010];
bool cmp(int x,int y) { return ar[x]<ar[y]; }
void sol(int l,int r,vector<int> &v) {
	if(v.size()==0) return;
	s1.clear(),s2.clear();
	int p=0;
	for(int i=1;i<v.size();++i) if(ar[v[i]]>ar[v[p]]) p=i;
	int q=v[p];
	for(int i=0;i<v.size();++i) if(i!=p) {
		printf("2 1 %d %d\n",q,v[i]);
		fflush(stdout);
		rd(ans);
		if(ans==1) s2.PB(v[i]);
		else s1.PB(v[i]);
	}
	sort(s2.begin(),s2.end(),cmp);
	sort(s1.begin(),s1.end(),cmp);
	v.clear();
	for(int i=0;i<s1.size();++i) v.PB(s1[i]);
	v.PB(q);
	for(int i=(int)s2.size()-1;i>=0;--i) v.PB(s2[i]);
}
int main() {
	rd(n);
	for(int i=3;i<=n;++i) {
		printf("2 1 2 %d\n",i);
		fflush(stdout);
		rd(ans);
		if(ans==1) v2.PB(i);
		else v1.PB(i);
		
		printf("1 1 2 %d\n",i);
		fflush(stdout);
		rd(ar[i]);
	}
	sol(1,2,v1);
	sol(2,1,v2);
	printf("0 ");
	printf("1 ");
	for(int i=0;i<v1.size();++i) printf("%d ",v1[i]);
	printf("2 ");
	for(int i=0;i<v2.size();++i) printf("%d ",v2[i]);
	return 0;
} 

D - Tree Queries

Sol

对询问分块,复杂度 O ( n log ⁡ n + n n ) O(n\log n + n\sqrt n) O(nlogn+nn )

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=150010,mod=998244353;
int Pow(int x,int y) {
	int res=1;
	while(y) {
		if(y&1) res=res*(ll)x%mod;
		x=x*(ll)x%mod,y>>=1;
	}
	return res;
}
int head[N],ecnt;
int sz[N],fa[N];
struct ed { int to,next; }e[N<<1];
void ad(int x,int y) {
	e[++ecnt]=(ed){y,head[x]}; head[x]=ecnt;
	e[++ecnt]=(ed){x,head[y]}; head[y]=ecnt;
}
namespace __ {
	int *U[N],*D[N];
	int dep[N],son[N],top[N];
	int dis[N];
	int p[N][19];
	void dfs1(int u,int last) {
		fa[u]=p[u][0]=last,dis[u]=dis[last]+1;
		sz[u]=1;
		for(int j=1;j<=18;++j) p[u][j]=p[p[u][j-1]][j-1];
		for(int k=head[u];k;k=e[k].next) {
			int v=e[k].to; if(v==last) continue;
			dfs1(v,u);
			if(dep[v]>=dep[u]) dep[u]=dep[v],son[u]=v;
			sz[u]+=sz[v];
		}
		dep[u]++;
	}
	void dfs2(int u,int tp) {
		top[u]=tp;
		if(son[u]) dfs2(son[u],tp);
		for(int k=head[u];k;k=e[k].next) {
			int v=e[k].to; if(v==p[u][0]||v==son[u]) continue;
			dfs2(v,v);
		}
	}
	void gao(int u) {
		U[u]=new int[dep[u]+1];
		D[u]=new int[dep[u]+1];
		int cur=u;
		for(int i=0;i<=dep[u];++i)
			D[u][i]=cur,cur=son[cur];
		cur=u;
		for(int i=0;i<=dep[u];++i)
			U[u][i]=cur,cur=p[cur][0];
	}
	int Lg[N];
	int query(int u,int k) {
		if(dis[u]<=k) return 0;
		if(!k) return u;
		u=p[u][Lg[k]],k-=(1<<Lg[k]);
		int td=dis[u]-dis[top[u]];
		if(td>=k) return D[top[u]][td-k];
		else return U[top[u]][k-td];
	}
}
struct Que {
	int u,d;
};
vector<Que> s;
int n,q;
int delt[N],del[N];
int ans[N];
void push_down(int u,int last) {
	del[u]=(del[last]+del[u])%mod;
	ans[u]=(ans[u]+del[u])%mod;
	for(int k=head[u];k;k=e[k].next) {
		int v=e[k].to; if(v==last) continue;
		push_down(v,u);
	}
	del[u]=0;
}
int get(int v,int u) {
	if(__::dis[v]>__::dis[u]) return sz[v];
	if(__::dis[v]==__::dis[u]) {
		if(v==u) return n;
		return sz[v];
	}
	int t=__::query(u,__::dis[u]-__::dis[v]-1);
	if(fa[t]==v) return n-sz[t];
	else return sz[v];
}
int main() {
	rd(n),rd(q);
	int inv=Pow(n,mod-2);
	for(int i=1,x,y;i<n;++i) rd(x),rd(y),ad(x,y);
	__::dfs1(1,0),__::dfs2(1,1);
	for(int i=1;i<=n;++i) if(__::top[i]==i) __::gao(i);
	for(int i=2;i<=n;++i) __::Lg[i]=__::Lg[i>>1]+1;
	for(int i=1;i<=q;++i) {
		int ty,v,d; rd(ty),rd(v);
		if(ty==1) {
			rd(d);
			delt[v]=(delt[v]+d)%mod;
			s.PB((Que){v,d});
		}
		else {
			ll t=ans[v];
			for(int j=0;j<s.size();++j)
				t=(t+get(s[j].u,v)*(ll)s[j].d)%mod;
			printf("%lld\n",(t*(ll)inv%mod+mod)%mod);
		}
		if(s.size()>400) {
			for(int u=1;u<=n;++u) if(delt[u]) {
				for(int k=head[u];k;k=e[k].next) {
					int v=e[k].to; if(v==fa[u]) continue;
					del[v]=(del[v]+(n-sz[v])*(ll)delt[u])%mod;
				}
				del[1]=(del[1]+sz[u]*(ll)delt[u])%mod;
				del[u]=(del[u]-sz[u]*(ll)delt[u])%mod;
				ans[u]=(ans[u]+n*(ll)delt[u])%mod;
				delt[u]=0;
			}
			push_down(1,0);
			s.clear();
		}
	}
	return 0;
}

E - Send Tree to Charlie

Sol

如果 ∑ d i s ( i , a i ) [ a i ≠ 0 ] > 2 n − 2 \sum dis(i,a_i)[a_i \not= 0] > 2n-2 dis(i,ai)[ai=0]>2n2必然无解,因为每条边最多能对这个式子产生 2 2 2的贡献。

所以可以暴力遍历 a i a_i ai i i i路径上的所有点。

相当于是限制了:

  • 对于路径上的第一个点,路径上的第一条边是与这个点相邻的所有边中第一个被操作的。
  • 对于路径中间的点 u u u,路径上与 u u u相邻的两条边,操作完第一条边之后操作的下一条与 u u u相邻的边必须是路径上的第二条边。
  • 对于路径上的最后一个点,路径上的最后一条边是与这个点相邻的所有边中最后一个被操作的。

同时我们发现,每个点的所有邻边的相对操作顺序确定,就可以唯一确定最终的 a i a_i ai

所以对每个点算答案然后乘起来就可以了。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define PB push_back
#define PII pair<int,int>
#define MP make_pair
#define fir first
#define sec second
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
void FAIL() { printf("0"); exit(0); }
const int N=5e5+10,mod=1e9+7;
int In[N],Out[N],To[N],vis[N],n;
int sol(vector<int> &G,vector<PII> &E) {
	G.PB(0),G.PB(n+1);
	for(int i=0;i<E.size();++i) Out[E[i].fir]++,In[E[i].sec]++,To[E[i].fir]=E[i].sec;
	for(int i=0;i<G.size();++i) if(In[G[i]]>1||Out[G[i]]>1) FAIL();
	for(int i=0;i<G.size();++i) if(!vis[G[i]]) {
		int cur=G[i]; vis[cur]=1;
		while(~To[cur]&&!vis[To[cur]]) cur=To[cur],vis[cur]=1;
		if(To[cur]==G[i]) FAIL();
	}
	int cur=0;
	while(~To[cur]) cur=To[cur];
	if(cur==n+1&&(int)G.size()-(int)E.size()>1) FAIL();
	
	for(int i=0;i<G.size();++i) In[G[i]]=Out[G[i]]=vis[G[i]]=0,To[G[i]]=-1;
	return max((int)G.size()-(int)E.size()-2,0);
}
vector<int> G[N];
vector<PII> E[N];
int fa[N],dep[N];
void dfs1(int u,int last) {
	fa[u]=last,dep[u]=dep[last]+1;
	for(int i=0;i<G[u].size();++i) {
		int v=G[u][i]; if(v==last) continue;
		dfs1(v,u);
	}
}
vector<int> P,Q;
void getpath(int x,int y) {
	P.clear(),Q.clear();
	int flg=0;
	if(dep[x]<dep[y]) swap(x,y),flg=1;
	P.PB(x);
	while(dep[x]>dep[y]) x=fa[x],P.PB(x);
	if(x==y) { if(flg) reverse(P.begin(),P.end()); return; }
	Q.PB(y);
	while(fa[x]!=fa[y]) x=fa[x],y=fa[y],P.PB(x),Q.PB(y);
	P.PB(fa[x]);
	for(int i=(int)Q.size()-1;i>=0;--i) P.PB(Q[i]);
	if(flg) reverse(P.begin(),P.end());
}
int tot;
int a[N];
int fac[N];
int main() {
	rd(n);
	for(int i=1,x,y;i<n;++i) rd(x),rd(y),G[x].PB(y),G[y].PB(x);
	dfs1(1,0);
	for(int i=1;i<=n;++i) rd(a[i]);
	for(int i=1;i<=n;++i) if(a[i]) {
		if(a[i]==i) FAIL();
		getpath(a[i],i);
		if((tot+=(int)P.size()-1)>2*n-2) FAIL();
		int m=(int)P.size()-1;
		E[P[0]].PB(MP(0,P[1]));
		E[P[m]].PB(MP(P[m-1],n+1));
		for(int i=1;i<m;++i) E[P[i]].PB(MP(P[i-1],P[i+1]));
	}
	memset(To,-1,sizeof(To));
	int ans=1;
	fac[0]=1; for(int i=1;i<=n;++i) fac[i]=fac[i-1]*(ll)i%mod;
	for(int i=1;i<=n;++i) ans=ans*(ll)fac[sol(G[i],E[i])]%mod;
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值