感觉这场比赛题目质量挺高(A 全场最佳),难度也不小。虽然 unr 后就懒得打了。
A. Inscribed Figures
题意
给你若干个图形,每个图形为三角形、圆形或正方形,第 (i) 个图形内接于第 (i-1) 个图形。问交点是否有限,如有限求交点个数。
(题目还有很多细节,具体见原题。)
题解
如果两个一样的图形相邻或正方形和三角形相邻。
圆和三角形有 (3) 个交点,和正方形有 (4) 个交点。
注意如果是 【圆、正方形、三角形】这样的,最上面有一个交点会重合,答案要减 1 。(出题人一开始也没注意到,后来重测导致unr)。
总体来说,这题真的没什么意思……
code
#include<bits/stdc++.h>
using namespace std;
int a[233];
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int n; scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
int ans=0,l=a[1];
for(int i=2;i<=n;++i)
{
int x=a[i];
if(x==l)
{
printf("Infinite");
return 0;
}
int t=x;
if(x>l) x=l,l=t;
if(x==1) ans+=l+1;
if(x==2) {
printf("Infinite");
return 0;
}
l=t;
}
for(int i=3;i<=n;++i) if(a[i-2]==3&&a[i-1]==1&&a[i]==2) --ans;
printf("Finite
%d",ans);
}
B. Ugly Pairs
题意
给你一个字符串 (S) ,重排该字符串使得任意两个在字母表中相邻的字符在字符串中不相邻。若无解输出 No answer .
(T) 组数据。 (T,|S| le 100) 。
题解
去重,只考虑字符的可重集。设 (n) 为去重后集合大小。
容易发现 (nle 3) 时会出现无解。
我们可以考虑构造。有很多种构造方案可以解决。对于 (n > 4) ,一种构造方案是每次选 (i) 和 (i+n/2) 放在一起。
那么对于 (nle 4) ,我们可以采用手玩或爆搜来解决。
code
#include<bits/stdc++.h>
using namespace std;
const int N=105;
char s[N]; bool flag,vis[30];
int n,m,num[30],ans[N];
void dfs(int u, int now)
{
if(flag) return ;
if(now>n)
{
flag=true;
for(int i=1;i<=n;++i)
while(num[ans[i]]--) putchar(ans[i]+'a');
puts("");
return ;
}
for(int i=0;i<26;++i)
{
if(!vis[i]&&num[i]&&i+1!=u&&i-1!=u)
{
vis[i]=true;
ans[now]=i;
dfs(i,now+1);
vis[i]=false;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("b.in","r",stdin);
#endif
int T; scanf("%d",&T);
while(T--)
{
flag=false;
scanf("%s",s+1);
n=strlen(s+1);
memset(num,0,sizeof(num));
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;++i) ++num[s[i]-'a'];
sort(s+1,s+1+n);
n=unique(s+1,s+1+n)-s-1;
if(n<=4)
{
dfs(233,1);
if(!flag) puts("No answer");
continue;
}
for(int i=1;i<=n>>1;++i)
{
while(num[s[i]-'a']--) putchar(s[i]);
while(num[s[i+(n-1)/2+1]-'a']--) putchar(s[i+(n-1)/2+1]);
}
if(n&1) while(num[s[n/2+1]-'a']--) putchar(s[n/2+1]);
puts("");
}
}
C. Match Points
题意
给你 (n) 个点 (x_1,x_2,...,x_n) 。两个点 (i,j) 能配对当且仅当 (|x_i-x_j|ge z) 。求最大配对数量。
(nle 2cdot 10^5, x_i,zle 10^9) 。
题解
首先直接顺次匹配肯定是错的,就不解释了。
考虑二分答案 (y) 。我们只要考虑前 (y) 个数,判断 (x_i) 能否和 (x_{n-y+i}) 匹配即可。
由这个二分答案的做法,我们可以进一步得出一个线性做法:我们可以把原序列分成两半然后顺次匹配,用双指针做一下就行了。
code
#include<bits/stdc++.h>
using namespace std;
inline int gi()
{
char c; int x=0,f=1;
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
return x*f;
}
const int N=2e5+5;
int a[N],n,z,ans;
int main()
{
#ifndef ONLINE_JUDGE
freopen("c.in","r",stdin);
#endif
n=gi(),z=gi();
for(int i=1;i<=n;++i) a[i]=gi();
sort(a+1,a+1+n);
int l=1,r=n/2+1;
while(l<=r&&l<=n/2&&r<=n)
{
if(a[r]-a[l]>=z) ++ans,++l;
++r;
}
printf("%d",ans);
}
D. 0-1-Tree
题意
给你一棵 (n) 个点的树,每条边标有 0 或 1 。问有多少对点 ((x,y)) 满足 (x ightarrow y) 的路径上 0 边不会在 1 边之后出现。
(nle 2cdot 10^5) 。
题解
先经过 0 边再经过 1 边。考虑枚举分界点,贡献即为 仅通过 0 边与其连通的节点数 * 仅通过 1 边与其连通的节点数。并查集维护即可。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int gi()
{
char c; int x=0,f=1;
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
return x*f;
}
const int N=200005;
int f[2][N],s[2][N];
int findset(int u, int w)
{
if(f[w][u]==u) return u;
return f[w][u]=findset(f[w][u],w);
}
void unionset(int u, int v, int w)
{
int fu=findset(u,w),fv=findset(v,w);
s[w][fu]+=s[w][fv];
f[w][fv]=fu;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("d.in","r",stdin);
#endif
int n=gi();
for(int i=0;i<2;++i)
for(int j=1;j<=n;++j) f[i][j]=j,s[i][j]=1;
for(int i=1;i<n;++i)
{
int u=gi(),v=gi(),w=gi();
unionset(u,v,w);
}
long long ans=0;
for(int i=1;i<=n;++i)
ans+=1ll*s[0][findset(i,0)]*s[1][findset(i,1)];
printf("%I64d",ans-n);
}
E. Special Segments of Permutation
题意
给你一个长度为 (n) 的排列 (p) ,询问有多少个区间 ([l,r]) ,满足 (displaystyle p_l+p_r=max_{i=l}^r p_i) 。
(nle 2cdot 10^5)
题解
考虑最大值分治。对于区间 ([l,r]) ,rmq 找到最大值 (m) ,我们选择 ([l,m-1]) 和 ([m+1,r]) 中长度最小的区间来枚举边界点。然后递归 ([l,m-1]) 和 ([m+1,r]) 。这样是启发式合并的复杂度 ,是 (O(nlog n)) 的 。
code
#include<bits/stdc++.h>
using namespace std;
inline int gi()
{
char c; int x=0,f=1;
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
return x*f;
}
const int N=2e5+5;
int f[N][30],p[N],pos[N],lg[N],ans,n;
inline int chmax(int x, int y) {
return p[x]>p[y]?x:y;
}
inline int getmx(int l, int r)
{
int t=lg[r-l+1];
return chmax(f[l][t],f[r-(1<<t)+1][t]);
}
void solve(int l, int r)
{
if(l>=r) return ;
int m=getmx(l,r);
if(m-l<r-m)
for(int i=l;i<m;++i)
ans+=(m<=pos[p[m]-p[i]]&&pos[p[m]-p[i]]<=r);
else
for(int i=m+1;i<=r;++i)
ans+=(l<=pos[p[m]-p[i]]&&pos[p[m]-p[i]]<=m);
solve(l,m-1),solve(m+1,r);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("e.in","r",stdin);
#endif
n=gi();
for(int i=2;i<=n;++i) lg[i]=lg[i-1]+!(i&(i-1));
for(int i=1;i<=n;++i) p[i]=gi(),pos[p[i]]=f[i][0]=i;
for(int j=1;(1<<j)<=n;++j)
for(int i=1;i+(1<<j)-1<=n;++i)
f[i][j]=chmax(f[i][j-1],f[i+(1<<j-1)][j-1]);
solve(1,n);
printf("%d",ans);
}
F. Card Bag
题意
你在玩游戏。给你 (n) 张牌,每张牌上有个数字 。你每次从中等概率选出一张牌并扔掉。设你选出的数为 (x) ,上一次选出的数为 (y) 。若 (x>y) 你输, (x=y) 你赢, (x<y) 继续。若牌扔完后游戏仍未结束则你输。求你赢的概率。
题解
我们需要选出一条单升序列,最后再选上序列中的最后一个数。
首先我们记下每个数字出现的次数,然后对原序列排序去重。
设 (f(i,j)) 表示前 (i) 个数选了 (j) 个的概率。
(f(i,j)=f(i-1,j)+f(i-1,j-1) imes frac{x_i}{n-j+1})
其中 (x_i) 表示这个数出现的个数。
统计答案的话在中间做一下就行了。
code
#include<bits/stdc++.h>
using namespace std;
inline int gi()
{
char c; int x=0,f=1;
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
return x*f;
}
const int N=5005,Mod=998244353;
int f[N][N],inv[N],num[N],a[N],n,m,ans;
int main()
{
#ifndef ONLINE_JUDGE
freopen("f.in","r",stdin);
#endif
n=m=gi();
for(int i=1;i<=n;++i) a[i]=gi(),++num[a[i]];
inv[0]=inv[1]=1;
for(int i=2;i<=n;++i) inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
sort(a+1,a+1+n);
n=unique(a+1,a+1+n)-a-1;
f[0][0]=1;
for(int i=1;i<=n;++i)
for(int j=0;j<=i;++j)
{
f[i][j]=1ll*num[a[i]]*inv[m-j+1]%Mod*f[i-1][j-1]%Mod;
ans=(ans+1ll*(num[a[i]]-1)*inv[m-j]%Mod*f[i][j]%Mod)%Mod;
f[i][j]=(f[i][j]+f[i-1][j])%Mod;
}
printf("%d",ans);
}