场次链接
略炸。
A. Shuffle Hashing
题目链接
一个字符串,只有’a’到’z’,你可以任意调整其顺序,然后在其前面和后面加上若干字符,给t组数据,每组给2个字符串,问下面这个字符串是否由上面这个变化而来,是输出YES,否则输出NO
数据范围 1 ≤ t ≤ 100 1\leq t\leq 100 1≤t≤100, 1 ≤ ∣ s 1 ∣ , ∣ s 2 ∣ ≤ 100 1\leq |s_1|,|s_2|\leq 100 1≤∣s1∣,∣s2∣≤100
解 将第一个串中每个字符有多少个存起来,然后在第二个字符串中,枚举连续的s1长度的字符串,判断每个字符数量是否相同即可。
复杂度 O ( n 2 ) O(n^2) O(n2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void work()
{
char a[105];
char b[105];
int c[26];
memset(c,0,sizeof(c));
int d[26];
memset(d,0,sizeof(d));
scanf("%s",a);
scanf("%s",b);
int la=strlen(a);
int lb=strlen(b);
for(int i=0;i<la;i++){
c[a[i]-'a']++;
}
for(int i=0;i+la<=lb;i++){
for(int j=0;j<la;j++){
d[b[i+j]-'a']++;
}
for(int j=0;j<26;j++){
if(c[j]!=d[j]){
break;
}
if(j==25){
printf("YES\n");
return;
}
}
memset(d,0,sizeof(d));
}
printf("NO\n");
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
scanf("%d",&T);
//cin>>T;
//T=1;
for(int i=1;i<=T;i++){
work();
}
}
B. A and B
题目链接
t组数据,每组给你x,y,你可以从1开始 依次将 1 , 2 , 3 , 4 , … … 1,2,3,4,…… 1,2,3,4,……加到x,y其中一个上,问最少要几步才能使得 x = y x=y x=y
数据范围 1 ≤ t ≤ 100 1\leq t\leq 100 1≤t≤100, 1 ≤ x , y ≤ 1 0 9 1\leq x,y\leq 10^9 1≤x,y≤109
解 可以将x直接减掉y,然后从1开始 你可以选择减去他或者加上他,最少要几步才能得到0。首先考虑 s u m ( 1 , n ) sum(1,n) sum(1,n),如果我把其中任意的一个数变成减,那么对和的影响是 2 , 4 , 6 , … … , 2 n 2,4,6,……,2n 2,4,6,……,2n,所以 如果刚好在 n n n时 s u m ( 1 , n ) sum(1,n) sum(1,n)大于 x − y x-y x−y,并且与 x − y x-y x−y的差为偶数,那么答案就是 n n n。
否则 考虑加上下一位,若为奇数,则使得 s u m ( 1 − n ) − ( x − y ) sum(1-n)-(x-y) sum(1−n)−(x−y)为偶数,回到上个情况,故答案为n+1,。若下一位为偶数,则需要加2个才能使得 s u m ( 1 − n ) − ( x − y ) sum(1-n)-(x-y) sum(1−n)−(x−y)为偶数,故答案是n+2。
复杂度 O ( x ) O(\sqrt{x}) O(x)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void work()
{
ll x,y;
scanf("%lld%lld",&x,&y);
if(x>y)swap(x,y);
ll z=y-x;
if(z==0){
printf("0\n");
return;
}
ll sum=0;
int cnt=0;
int tmp=0;
for(int i=1;;i++){
sum+=i;
cnt++;
if(sum>=z){
tmp=i;
break;
}
}
if((sum-z)%2==0){
printf("%d\n",cnt);
}else{
if(tmp%2==0){
printf("%d\n",cnt+1);
}else{
printf("%d\n",cnt+2);
}
}
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
scanf("%d",&T);
//cin>>T;
//T=1;
while(T--){
work();
}
}
C. Berry Jam
题目链接
t组数据,每组数据有2*n个罐子,每个罐子分为草莓和蓝莓,1为草莓,2为蓝莓,你可以从中间开始,拿走两边最靠近你的罐子,问你最少几次操作使得两种罐子数量相等。
数据范围 1 ≤ t ≤ 1000 1\leq t\leq 1000 1≤t≤1000, 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1≤n≤105, 1 ≤ a i ≤ 2 1\leq a_i\leq 2 1≤ai≤2
解 设草莓为1,蓝莓为-1,用map存从n+1开始,获得每个值的最少时间。然后枚举左端点,log在map中查找需要在右边拿的最少时间,求最小值即可。
需要特判一下右边不拿的情况。
复杂度 O ( n log ) O(n\log) O(nlog)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int cnt=0;
int a[200005];
void work()
{
int n;
scanf("%d",&n);
int suma=0;
int sumb=0;
for(int i=1;i<=2*n;i++){
scanf("%d",&a[i]);
if(a[i]==1){
suma++;
}else{
sumb++;
}
}
int tmp=suma-sumb;
int cnt=0;
map<int,int>m;
for(int i=n+1;i<=2*n;i++){
if(a[i]==1){
cnt++;
}else{
cnt--;
}
if(m[cnt]==0){
m[cnt]=i-n;
}
//printf("%d ",cnt);
}
int ans=9999999;
if(tmp==0){
printf("0\n");return;
}
if(m[tmp]){
ans=m[tmp];
}
for(int i=n;i>=1;i--){
if(a[i]==1){
tmp--;
}else{
tmp++;
}
if(tmp==0){
ans=min(ans,n-i+1);
}
if(m[tmp]!=0){
ans=min(ans,n-i+1+m[tmp]);
}
}
printf("%d\n",ans);
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
scanf("%d",&T);
//cin>>T;
//T=1;
for(int i=1;i<=T;i++){
work();
}
}
D. Segment Tree
题目链接
给你n个线段,每个线段有 l i , r i l_i,r_i li,ri,如果两个线段有相交,但不完全包含,那么这两个线段之间有边,问最后组成的是否是一棵树。
数据范围 1 ≤ n ≤ 5 ∗ 1 0 5 1\leq n\leq 5*10^5 1≤n≤5∗105, 1 ≤ l i , r i ≤ 2 n 1\leq l_i,r_i\leq 2n 1≤li,ri≤2n
解 首先要为一棵树的话,最后的边数要为 n − 1 n-1 n−1,且不能有环。先将数据按 l l l排序,然后将数据的 r , i d r,id r,id依次插入set,插入之前用二分找到第一个能与该线段连上边的位置,然后遍历,将连上的点进行并查集,如果连到2个已经在同一个并查集中的点,那么就有环,返回NO,如果边数不为n-1,也输出NO,否则输出YES
复杂度 O ( n log ) O(n\log) O(nlog)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node
{
int l;
int r;
int id;
}num[500005];
bool cmp(node x,node y)
{
return x.l<y.l;
}
int f[500005];
int find(int x)
{
if(x!=f[x]){
return f[x]=find(f[x]);
}
return f[x];
}
void uni(int x,int y)
{
int x1=find(x);
int y1=find(y);
if(x1!=y1){
f[x1]=y1;
}
}
void work()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&num[i].l,&num[i].r);
num[i].id=i;
f[i]=i;
}
sort(num+1,num+n+1,cmp);
set<pair<int,int> >s;
set<pair<int,int> >::iterator it;
int sum=0;
for(int i=1;i<=n;i++){
if(sum>n-1){
printf("NO\n");return;
}
pair<int,int> k;
k.first=num[i].l;
k.second=num[i].id;
it=s.lower_bound(k);
while(it!=s.end()){
//printf("%d %d\n",(*it).first,num[i].r);
if((*it).first>num[i].r){
break;
}
// printf("???%d %d\n",(*it).second,num[i].id);
if(find(num[i].id)==find((*it).second)){
printf("NO\n");return;
}else{
uni(num[i].id,(*it).second);
sum++;
}
it++;
}
k.first=num[i].r;
s.insert(k);
}
//printf("%d\n",sum);
if(sum!=n-1){
printf("NO\n");
}else{
printf("YES\n");
}
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
//scanf("%d",&T);
//cin>>T;
T=1;
while(T--){
work();
}
}
E. Tests for problem D
与上题完全相反,给你一些边的关系,让你给出所有点的线段,使得满足这些关系。
数据范围 1 ≤ n ≤ 5 ∗ 1 0 5 1\leq n\leq 5*10^5 1≤n≤5∗105, 1 ≤ x i , y i ≤ 2 ∗ n 1\leq x_i,y_i\leq 2*n 1≤xi,yi≤2∗n
解 vector建边,然后dfs,将点的左、右尽量靠近父节点的右边即可。
复杂度 O ( n ) O(n) O(n)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>v[1000005];
int l[1000005];
int r[1000005];
int dfs(int past,int now)
{
//printf("%d %d %d\n",past,now,v[now].size());
int sz=1;
int tmp=r[now]-1;
for(int i=0;i<v[now].size();i++){
if(v[now][i]==past)continue;
l[v[now][i]]=tmp;
r[v[now][i]]=tmp+sz*2-1+v[v[now][i]].size();
sz+=dfs(now,v[now][i]);
tmp--;
}
return sz;
}
void work()
{
int n;
scanf("%d",&n);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
l[1]=1;
r[1]=v[1].size()+2;
dfs(0,1);
for(int i=1;i<=n;i++){
printf("%d %d\n",l[i],r[i]);
}
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
//scanf("%d",&T);
//cin>>T;
T=1;
while(T--){
work();
}
}
F. Cards
题目链接
m张牌中有一张王牌,有n次操作,每次操作将牌打乱顺序,拿出最顶上那张牌,记录后放回去。x为n次操作后拿出王牌的次数,问x^k的期望值。答案对998244353取模。
数据范围 1 ≤ n , m ≤ 998244353 1\leq n,m\leq 998244353 1≤n,m≤998244353, 1 ≤ k ≤ 5000 1\leq k\leq 5000 1≤k≤5000
解 首先列出式子 ∑ i = 1 n C n i ∗ i k ∗ ( m − 1 ) n − i m n \frac{\sum_{i=1}^{n}C_n^i*i^k*(m-1)^{n-i}}{m^n} mn∑i=1nCni∗ik∗(m−1)n−i。
考虑分子部分,根据第二类斯特林数可得 n k = ∑ i = 0 n C n i ∗ S ( k , i ) ∗ i ! n^k=\sum_{i=0}^{n}C_n^i*S(k,i)*i! nk=∑i=0nCni∗S(k,i)∗i!。
代入分子可得, ∑ i = 1 n C n i ∗ ( m − 1 ) n − i ∑ j = 0 m i n ( i , k ) C n j ∗ S ( k , j ) ∗ j ! \sum_{i=1}^nC_n^i*(m-1)^{n-i}\sum_{j=0}^{min(i,k)}C_n^j*S(k,j)*j! ∑i=1nCni∗(m−1)n−i∑j=0min(i,k)Cnj∗S(k,j)∗j!
然后交换求和得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) ∗ j ! ∑ i = j n C n i ∗ C i j ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j)*j!\sum_{i=j}^nC_n^i*C_i^j*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)∗j!∑i=jnCni∗Cij∗(m−1)n−i
将组合数拆开 进行化简得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) ∑ i = j n n ! ( n − i ) ! ∗ ( i − j ) ! ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j)\sum_{i=j}^n{\frac{n!}{(n-i)!*(i-j)!}}*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)∑i=jn(n−i)!∗(i−j)!n!∗(m−1)n−i
那么就是要解决里面这个n的求和 进行一点点的变换 ∑ j = 0 m i n ( n , k ) S ( k , j ) ∑ i = j n n ! ( n − j ) ! ( n − j ) ! ( n − i ) ! ∗ ( i − j ) ! ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j)\sum_{i=j}^n{{\frac{n!}{(n-j)!}}\frac{(n-j)!}{(n-i)!*(i-j)!}}*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)∑i=jn(n−j)!n!(n−i)!∗(i−j)!(n−j)!∗(m−1)n−i
提出 n ! ( n − j ) ! {\frac{n!}{(n-j)!}} (n−j)!n!得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∑ i = j n ( n − j ) ! ( n − i ) ! ∗ ( i − j ) ! ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!}}\sum_{i=j}^n{\frac{(n-j)!}{(n-i)!*(i-j)!}}*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)(n−j)!n!∑i=jn(n−i)!∗(i−j)!(n−j)!∗(m−1)n−i
得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∑ i = j n C n − j i − j ∗ ( m − 1 ) n − i \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!}}\sum_{i=j}^nC_{n-j}^{i-j}*(m-1)^{n-i} ∑j=0min(n,k)S(k,j)(n−j)!n!∑i=jnCn−ji−j∗(m−1)n−i
将i-j替换为i得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∑ i = 0 n − j C n − j i ∗ ( m − 1 ) n − i − j \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!}}\sum_{i=0}^{n-j}C_{n-j}^{i}*(m-1)^{n-i-j} ∑j=0min(n,k)S(k,j)(n−j)!n!∑i=0n−jCn−ji∗(m−1)n−i−j
里面就是一个二项式定理求和 得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∗ m n − j \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!}}*m^{n-j} ∑j=0min(n,k)S(k,j)(n−j)!n!∗mn−j
把分母算上 得到 ∑ j = 0 m i n ( n , k ) S ( k , j ) n ! ( n − j ) ! ∗ m j \sum_{j=0}^{min(n,k)}S(k,j){\frac{n!}{(n-j)!*m^j}} ∑j=0min(n,k)S(k,j)(n−j)!∗mjn!
复杂度 O ( k 2 ) O(k^2) O(k2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p=998244353;
ll dp[5005][5005];
ll qpow(ll a,ll n)
{
ll ret=1;
while(n){
if(n&1)ret=ret*a%p;
a=a*a%p;
n>>=1;
}
return ret;
}
void work()
{
ll n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
dp[0][0]=1;
for(int i=1;i<=k;i++){
dp[i][0]=0;
for(int j=1;j<=i;j++){
dp[i][j]=(dp[i-1][j]*j+dp[i-1][j-1])%p;
}
}
ll tmp=1;
ll invm=qpow(m,p-2);
ll ans=0;
for(int i=1;i<=k;i++){
tmp=tmp*(n+1-i)%p;
ans=(ans+tmp*qpow(invm,i)%p*dp[k][i]%p)%p;
}
printf("%lld\n",ans);
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
//scanf("%d",&T);
//cin>>T;
T=1;
while(T--){
work();
}
}