Grakn Forces 题解

目前进度:A~G

本来打得还行的一场,D 交了三发罚时,FG 两道蠢题还没做出来……

最草的是 B 写挂了过了 pretest……看到有人在 hack 还试着锁了一下,然后就 % Alex_Wei 了(

(讲真感觉 A~E 中也就 B 我比较喜欢吧,比剩下的质量不知道高到哪里去了

不过掉的不多,海星。


A

一位一位填,选任意一个目前不会冲突的数。因为有三个候选,而只有两个邻居,所以一定能有可以选的候选。

时间复杂度 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,a[3][maxn],ans[maxn];
void solve(){
	n=read();
	FOR(_,0,2) FOR(i,1,n) a[_][i]=read();
	FOR(i,1,n) ans[i]=0;
	FOR(i,1,n){
		int prv=i==1?n:i-1,nxt=i==n?1:i+1;
		FOR(j,0,2) if(a[j][i]!=ans[prv] && a[j][i]!=ans[nxt]){ans[i]=a[j][i];break;}
	}
	FOR(i,1,n) printf("%d ",ans[i]);
	puts("");
}
int main(){
	int T=read();
	while(T--) solve();
}

B

感觉这难度很不止 B 啊……还出了一堆锅,isaf27 赔钱(

显然考虑差分。把每个数组都差分。

第一位无关紧要(可以看成把第一位任意分配,然后把所有数都减去第一个数,显然答案不变)。

接下来都不考虑第一位。

现在对每个 \(b_i\) 序列的限制变成了:非零数的个数不超过 \(k-1\)

\(a_j=0\),显然让每个 \(b_{i,j}=0\) 最优。

\(a_j\ne 0\),显然让其中一个 \(b_{i,j}=a_j\),其它都是 \(0\) 最优。

那么令 \(c\)\(a_j\ne 0\) 的个数,既然每个序列至多 \(k-1\) 个,所以至少要 \(\lceil\frac{c}{k-1}\rceil\) 个。

特判 \(k=1\)

特判 \(c=0\),答案不能是 \(0\)(就是这个把我送走了……)

时间复杂度 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,k,a[maxn];
void solve(){
	n=read();k=read();
	FOR(i,1,n) a[i]=read();
	int cnt=0;
	FOR(i,2,n) if(a[i]!=a[i-1]) cnt++;
	if(k==1){
		if(cnt) puts("-1");
		else puts("1"); 
	}
	else printf("%d\n",max(1,(cnt+k-2)/(k-1)));
}
int main(){
	int T=read();
	while(T--) solve();
}

C

这很应该 swap(B,C) 吧……

为了方便,加 \(0\)\(l\) 两个点。

\(t1_i\) 表示第一个人走到第 \(i\) 个的用时,从上一个点按新速度转移过来即可。

\(t2_i\) 类似。

接下来两人从两边分别走,每次选择最先到达下一个点的人走,直到他们相邻。

此时再大力上式子。小学课内数学,不再赘述。

时间复杂度 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,l,a[maxn];
double t1[maxn],t2[maxn];
void solve(){
	n=read();l=read();
	FOR(i,1,n) a[i]=read();
	a[n+1]=l; 
	FOR(i,1,n+1) t1[i]=t1[i-1]+1.0*(a[i]-a[i-1])/i;
	ROF(i,n,0) t2[i]=t2[i+1]+1.0*(a[i+1]-a[i])/(n-i+1);
	int l=0,r=n+1;
	while(l+1<r){
		if(t1[l+1]<t2[r-1]) l++;
		else r--;
	}
	printf("%.10lf\n",max(t1[l],t2[r])+1.0*(a[r]-a[l]-fabs(t1[l]-t2[r])*(t1[l]<t2[r]?l+1:n-r+2))/(l+1+n-r+2));
	FOR(i,1,n) a[i]=t1[i]=t2[i]=0;
}
int main(){
	int T=read();
	while(T--) solve(); 
}

D

设向上 \(x\) 步,向右 \(y\) 步(显然操作顺序不影响)。

枚举 \(i,j\)。如果第 \(i\) 个已经不能被第 \(j\) 个看到,那就大棒子。否则需要满足 \(x\ge c_j-a_i+1\)\(y\ge d_j-b_i+1\)

\(p_{i,j}=c_j-a_i+1,q_{i,j}=d_j-b_i+1\)(只保留一开始能看到的对),把这 \(O(nm)\) 对排个序(或者开桶也是可以的)。

枚举 \(x\),那么会满足 \(p_{i,j}\le x\) 的所有对(所以显然,要么 \(x=0\),要么 \(x\) 是某个 \(p_{i,j}\))。

对于 \(p_{i,j}>x\) 的所有对,必须满足 \(y\ge q_{i,j}\)。因为排过序了形成了个后缀,\(y\) 就是 \(q_{i,j}\) 的后缀最大值。

我比较无脑所以直接排序了,时间复杂度 \(O(nm\log nm)\),不过好像飞快就不管了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=2020,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,m,a[maxn],b[maxn],c[maxn],d[maxn],pl,suf[maxn*maxn],ans=1e9;
PII p[maxn*maxn];
int main(){
	n=read();m=read();
	FOR(i,1,n) a[i]=read(),b[i]=read();
	FOR(i,1,m) c[i]=read(),d[i]=read();
	FOR(i,1,n) FOR(j,1,m){
		if(c[j]<a[i]) continue;
		if(d[j]<b[i]) continue; 
		p[++pl]=MP(c[j]-a[i]+1,d[j]-b[i]+1);
	}
	sort(p+1,p+pl+1);
//	FOR(i,1,pl) printf("(%d,%d)\n",p[i].first,p[i].second);
	ROF(i,pl,1) suf[i]=max(suf[i+1],p[i].second);
	FOR(i,1,pl) if(i==pl || p[i].first!=p[i+1].first) ans=min(ans,p[i].first+suf[i+1]);
	if(!pl) ans=0;
	ans=min(ans,p[pl].first);
	ans=min(ans,suf[1]);
	printf("%d\n",ans);
}

E

凭啥这个会比 F 过的还少啊 /yiw

将一个集合内的点连成完全图太烦了,不如每个集合建个虚点,并将所有这里面的数和这个虚点连边。

实际上就是建出二分图(

那么原来的一个彩虹环会一一对应现在的简单环。

要没有环,每个连通块保留最大生成树,把剩下的边删掉即可。

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

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=222222,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
struct edge{
	int u,v,w;
	bool operator<(const edge &e)const{
		return w>e.w;
	}
}e[maxn];
int n,m,a[maxn],b[maxn],el,fa[maxn];
ll ans;
int getfa(int x){
	return x==fa[x]?x:fa[x]=getfa(fa[x]);
}
int main(){
	n=read();m=read();
	FOR(i,1,n) a[i]=read();
	FOR(i,1,m) b[i]=read();
	FOR(i,1,n){
		int l=read();
		while(l--){
			int x=read();
			e[++el]=(edge){i,x+n,a[i]+b[x]};
			ans+=a[i]+b[x];
		}
	}
	sort(e+1,e+el+1);
	FOR(i,1,n+m) fa[i]=i;
	FOR(i,1,el){
		int u=e[i].u,v=e[i].v,w=e[i].w;
		u=getfa(u);v=getfa(v);
		if(u==v) continue;
		fa[u]=v;
		ans-=w;
	}
	printf("%lld\n",ans);
}

F

推了一整场的假结论,不知道可以把已经搞定的元素变化,白给……

对于 \(n=2^k\)\(k\) 是整数),是可以做到所有数一样的。

分治,左边和右边一一配对就行了,操作次数 \(\frac{1}{2}n\log n\)。归纳可以证明。

现在,找到不超过 \(n\) 的最大的 \(2^k\),把前 \(2^k\) 变成一样的,再把后 \(2^k\) 变成一样的,就行了。

操作次数 \(n\log n\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=555555,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,lim=1,ans[maxn][2],al;
void work(int l,int r){
	if(l==r) return;
	int mid=(l+r)>>1;
	work(l,mid);work(mid+1,r);
	FOR(i,l,mid) ans[++al][0]=i,ans[al][1]=mid+i-l+1;
}
int main(){
	n=read();
	while(lim<=n) lim<<=1;
	lim>>=1;
	work(1,lim);work(n-lim+1,n);
	printf("%d\n",al);
	FOR(i,1,al) printf("%d %d\n",ans[i][0],ans[i][1]);
}

G

想当年初二的时候,我还能把一个看起来和 kruskal 重构树毫无关系的东西转换成类似 kruskal 重构树的东西……这就是退役的前兆吗 /kk

看成 \(n\) 个点的完全图,那么选出来的每个集合所构成的完全图,里面的边权都要小于与相邻点的边权。

直接 kruskal 重构树。判断每个子树是否能是个完全图,然后直接 DP。

时间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1555,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
struct edge{
	int u,v,w;
	bool operator<(const edge &e)const{
		return w<e.w;
	}
}e[maxn*maxn/2];
int n,el,cnt,fa[maxn*2],f[maxn*2][maxn],sz[maxn*2],ls[maxn*2],rs[maxn*2],ec[maxn*2];
int getfa(int x){
	return x==fa[x]?x:fa[x]=getfa(fa[x]);
}
void dfs(int u){
	if(ls[u] || rs[u]){
		assert(ls[u] && rs[u]);
		dfs(ls[u]);dfs(rs[u]);
		sz[u]+=sz[ls[u]]+sz[rs[u]];
		FOR(i,1,sz[ls[u]]) FOR(j,1,sz[rs[u]])
			f[u][i+j]=(f[u][i+j]+1ll*f[ls[u]][i]*f[rs[u]][j])%mod; 
	}
	else sz[u]=1;
	if(ec[u]==sz[u]*(sz[u]-1)/2) f[u][1]=(f[u][1]+1)%mod;
}
int main(){
	cnt=n=read();
	FOR(i,1,n) FOR(j,1,n){
		int x=read();
		if(i<j) e[++el]=(edge){i,j,x};
	}
	sort(e+1,e+el+1);
	FOR(i,1,2*n) fa[i]=i;
	FOR(i,1,el){
		int u=e[i].u,v=e[i].v;
		u=getfa(u);v=getfa(v);
		if(u==v){ec[u]++;continue;}
		fa[u]=fa[v]=++cnt;
		ls[cnt]=u;rs[cnt]=v;
		ec[cnt]=ec[u]+ec[v]+1;
	}
	dfs(cnt);
	FOR(i,1,n) printf("%d ",f[cnt][i]);
}
posted @ 2020-10-01 10:36  ATS_nantf  阅读(246)  评论(0编辑  收藏  举报