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]);
}