0%

[GYM102994] 脑瘫题两则

能不能正赛场上不要脑瘫啊。

简单题做不出来直接崩了/kk。

I. A Math Problem

题意

现在有 个人和 和球队,满足如下条件:

  1. 任何一个人都至少是一个球队的球迷,并且没有人同时是所有球队的球迷;
  2. 对于任意两个球队 ,都恰好存在一个球队 ,满足球队 的球迷恰好是 的并;
  3. 对于任意两个球队 ,都恰好存在一个球队 ,满足球队 的球迷恰好是 的交;

计算方案数。多测。

题解

考虑球迷最多的队伍 和任意一个其他队伍

此时一定要存在一个队伍 ,使得 的球迷是 的并,那么 的球迷个数显然是 的。

所以唯一的可能是 ,这说明 的球迷应该包含其他所有球队的球迷。

又因为每一个人至少是一个球队的球迷,所以所有人肯定都是 的球迷。

所以意思就是说,肯定有一个球队,使得 个人都是他的球迷。

同理,肯定还有一个球队,没有球迷。

所以现在已经只需要确定剩下 个队伍的方案数。

考虑直接暴搜。

此时所有的人可以被分为 类。

那么直接 枚举一下,每一类人的个数是 还是

然后暴力判断是否合法即可。

后面还需要计算把 个人分入 组的方案数,可能需要一个容斥。

时间复杂度

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fll
#define debug(x) cerr<<#x<<"="<<x<<endl
using namespace std;
using ll=long long;
using ld=long double;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline ll lread()
{
ll s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
const int mod=1000000007;
const int L=20;
const int P=4;
bool vis[100005];
ll C[25][25];
ll dp[25][25];
int v[6];
inline ll qow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void dfs(int num,int now)
{
if(num==(1<<P))
{
if(!now) return ;
for(int j=0;j<P;j++) vis[v[j]]=1;
vis[0]=vis[now]=1;
for(int j=1;j<=P;j++)
{
//[0,j-1]
bool f=1;
for(int p=0;p<j;p++) if(!v[p]||v[p]==now) f=0;
for(int p=j;p<P;p++) if(v[p]) f=0;
for(int p=0;p<j;p++) for(int q=p+1;q<j;q++)
{
if(v[p]==v[q]) f=0;
if(!vis[v[p]&v[q]]) f=0;
if(!vis[v[p]|v[q]]) f=0;
}
if(f) dp[j+2][__builtin_popcount(now)]++;
}
for(int j=0;j<P;j++) vis[v[j]]=0;
vis[0]=vis[now]=0;
return ;
}
for(int i=0;i<2;i++)
{
int V=i<<num;
for(int j=0;j<P;j++) if(num&(1<<j)) v[j]^=V;
dfs(num+1,now|V);
for(int j=0;j<P;j++) if(num&(1<<j)) v[j]^=V;
}
}
int main()
{
for(int i=0;i<=L;i++)
{
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
dfs(0,0);

int T=read();
while(T--)
{
ll n=lread();int m=read();ll ans=0;
if(m==2)
{
printf("2\n");
continue;
}
for(int i=1;i<=L;i++) if(dp[m][i])
{
//n个人分入i组
ll V=0;
for(int j=0;j<=i;j++)
{
ll val=qow(i-j,n)*C[i][j]%mod;
if(j&1) V-=val;
else V+=val;
}
(ans+=dp[m][i]*V)%=mod;
}
ans=(ans%mod+mod)%mod;
ans=ans*m%mod*(m-1)%mod;
printf("%lld\n",ans);
}
return 0;
}

B. Gifted Composer

题意

次操作和一个初始为空的字符串。

每一次操作是往这个字符串开头或结尾加入一个字符。

每次操作结束后输出当前的字符串有多少种循环节。

题解

我觉得这没做出来也太脑瘫了

考虑一个长度 在什么时候会是这个字符串的循环节。

可以发现,这个范围一定是一段区间,并且这个区间的左端点为

考虑二分右端点,问题变为如何判断一个字符串是否有长度为 的循环节。

假设这个字符串为

时,可以发现只需要判断是否有

时,先把前面完整的段判完,在单独判后面一小段即可。

哈希,启动。时间复杂度

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fll
#define debug(x) cerr<<#x<<"="<<x<<endl
using namespace std;
using ll=long long;
using ld=long double;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
const int mod=1004535809;
const int base=173;
int pre[1000005];
int a[2000005];
ll h[2000005];
ll p[2000005];
int l[1000005];
int r[1000005];
char s[20];
int n;
inline int get1()
{
scanf("%s",s+1);
if(s[1]=='p') return 0;
return 1;
}
inline int get2()
{
scanf("%s",s+1);
if(s[1]=='d') return 1;
if(s[1]=='r') return 2;
if(s[1]=='m') return 3;
if(s[1]=='f') return 4;
if(s[2]=='o') return 5;
if(s[1]=='l') return 6;
return 7;
}
inline ll get(int l,int r)
{
ll ans=h[r]-h[l-1]*p[r-l+1];
return (ans%mod+mod)%mod;
}
inline bool check(int len,int id)
{
int L=l[id],R=r[id];
// [L,L+len-1]
int num=(R-(L+len-1))/len,RR=L+(num+1)*len-1;
if(get(L,RR-len)!=get(L+len,RR)) return false;
int s=R-RR;
return get(RR+1,R)==get(L,L+s-1);
}
inline ll get(int len)
{
int ans=len;
for(int i=524288;i;i>>=1) if(ans+i<=n&&check(len,ans+i)) ans+=i;
return ans;
}
int main()
{
n=read();
get1();l[1]=r[1]=n;a[n]=get2()+114;
for(int i=2;i<=n;i++)
{
int op=get1();
if(op==1)
{
l[i]=l[i-1]-1,r[i]=r[i-1];
a[l[i]]=get2()+114;
}
else
{
l[i]=l[i-1],r[i]=r[i-1]+1;
a[r[i]]=get2()+114;
}
}
p[0]=1;
for(int i=1;i<2*n;i++)
{
h[i]=(h[i-1]*base+a[i])%mod;
p[i]=p[i-1]*base%mod;
}

for(int i=1;i<=n;i++) pre[i]++,pre[get(i)+1]--;
for(int i=1;i<=n;i++) pre[i]+=pre[i-1],printf("%d\n",pre[i]);
return 0;
}