感受这场比赛题目质量挺高(A 全场最佳),难度也不小。虽然 unr 后就懒得打了。c++
给你若干个图形,每一个图形为三角形、圆形或正方形,第 $i$ 个图形内接于第 $i-1$ 个图形。问交点是否有限,若有限求交点个数。spa
(题目还有不少细节,具体见原题。)指针
若是两个同样的图形相邻或正方形和三角形相邻。code
圆和三角形有 $3$ 个交点,和正方形有 $4$ 个交点。排序
注意若是是 【圆、正方形、三角形】这样的,最上面有一个交点会重合,答案要减 1 。(出题人一开始也没注意到,后来重测致使unr)。递归
整体来讲,这题真的没什么意思……游戏
#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\n%d",ans); }
给你一个字符串 $S$ ,重排该字符串使得任意两个在字母表中相邻的字符在字符串中不相邻。若无解输出 No answer .ci
$T$ 组数据。 $T,|S| \le 100$ 。字符串
去重,只考虑字符的可重集。设 $n$ 为去重后集合大小。get
容易发现 $n\le 3$ 时会出现无解。
咱们能够考虑构造。有不少种构造方案能够解决。对于 $n > 4$ ,一种构造方案是每次选 $i$ 和 $i+n/2$ 放在一块儿。
那么对于 $n\le 4$ ,咱们能够采用手玩或爆搜来解决。
#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(""); } }
给你 $n$ 个点 $x_1,x_2,...,x_n$ 。两个点 $i,j$ 能配对当且仅当 $|x_i-x_j|\ge z$ 。求最大配对数量。
$n\le 2\cdot 10^5, x_i,z\le 10^9$ 。
首先直接顺次匹配确定是错的,就不解释了。
考虑二分答案 $y$ 。咱们只要考虑前 $y$ 个数,判断 $x_i$ 可否和 $x_{n-y+i}$ 匹配便可。
由这个二分答案的作法,咱们能够进一步得出一个线性作法:咱们能够把原序列分红两半而后顺次匹配,用双指针作一下就好了。
#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); }
给你一棵 $n$ 个点的树,每条边标有 0 或 1 。问有多少对点 $(x,y)$ 知足 $x\rightarrow y$ 的路径上 0 边不会在 1 边以后出现。
$n\le 2\cdot 10^5$ 。
先通过 0 边再通过 1 边。考虑枚举分界点,贡献即为 仅经过 0 边与其连通的节点数 * 仅经过 1 边与其连通的节点数。并查集维护便可。
#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); }
给你一个长度为 $n$ 的排列 $p$ ,询问有多少个区间 $[l,r]$ ,知足 $\displaystyle p_l+p_r=\max_{i=l}^r p_i$ 。
$n\le 2\cdot 10^5$
考虑最大值分治。对于区间 $[l,r]$ ,rmq 找到最大值 $m$ ,咱们选择 $[l,m-1]$ 和 $[m+1,r]$ 中长度最小的区间来枚举边界点。而后递归 $[l,m-1]$ 和 $[m+1,r]$ 。这样是启发式合并的复杂度 ,是 $O(n\log n)$ 的 。
#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); }
你在玩游戏。给你 $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)\times \frac{x_i}{n-j+1}$
其中 $x_i$ 表示这个数出现的个数。
统计答案的话在中间作一下就好了。
#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); }