A.牛妹的游戏
Ramsey定理:人话解释任意六个人中要么至少三个人认识,要么至少三个不认识。
假设 6 6 6 个据点分别为 A , B , C , D , E , F A,B,C,D,E,F A,B,C,D,E,F那么在 A 连向其它据点的控制链中,必然至少有 3 3 3条链被同一方控制,不妨假设它们为 A B , A C , A D AB,AC,AD AB,AC,AD。如此一来只要 B C , B D , C D BC,BD,CD BC,BD,CD 中有任意一条链也被这一方控制,则可以形成控制区域;如果这三条链都没有被这一方控制,也就意味着它们都被对方控制了,则它们同样可以形成控制区域。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N=500010;
int n,m;
int e[10][10];
int main()
{
IO;
int T=1;
cin>>T;
while(T--)
{
cin>>n>>m;
memset(e,0,sizeof e);
if(n>5)
{
for(int i=1;i<=m;i++)
{
int a,b;
cin>>a>>b;
}
cout<<"yes\n";
continue;
}
for(int i=1;i<=m;i++)
{
int a,b;
cin>>a>>b;
e[a][b]=e[b][a]=1;
}
bool ok=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
for(int k=j+1;k<=n;k++)
if(e[i][j]&&e[j][k]&&e[k][i]||!e[i][j]&&!e[j][k]&&!e[k][i])
ok=1;
if(ok) cout<<"yes\n";
else cout<<"no\n";
}
return 0;
}
B.病毒扩散
打表找规律,杨辉三角。
第四秒后扩张现象如下图
[
1
×
1
4
×
1
6
×
1
4
×
1
1
×
1
4
×
1
6
×
2
4
×
3
1
×
4
6
×
1
4
×
3
1
×
6
4
×
1
1
×
4
1
×
1
]
\begin{bmatrix} 1×1&4×1&6×1&4×1&1×1\\4×1&6×2&4×3&1×4\\6×1&4×3&1×6\\4×1&1×4\\1×1 \end{bmatrix}
⎣⎢⎢⎢⎢⎡1×14×16×14×11×14×16×24×31×46×14×31×64×11×41×1⎦⎥⎥⎥⎥⎤
好像不是那么明显,我们把乘号左边和右边的数分别拿出来
左
边
:
[
1
4
6
4
1
4
6
4
1
6
4
1
4
1
1
]
左边:\begin{bmatrix} 1&4&6&4&1\\4&6&4&1\\6&4&1\\4&1\\1 \end{bmatrix}
左边:⎣⎢⎢⎢⎢⎡146414641641411⎦⎥⎥⎥⎥⎤
右
边
:
[
1
1
1
1
1
1
2
3
4
1
3
6
1
4
1
]
右边:\begin{bmatrix} 1&1&1&1&1\\1&2&3&4\\1&3&6\\1&4\\1 \end{bmatrix}
右边:⎣⎢⎢⎢⎢⎡111111234136141⎦⎥⎥⎥⎥⎤
不难发现这两个矩阵的这些数和组合数(杨辉三角)有关
考虑位置为
(
x
,
y
)
(x,y)
(x,y)时间是
t
t
t的情况下:
左边的数
C
t
x
+
y
C_{t}^{x+y}
Ctx+y,右边的数
C
x
+
y
x
C_{x+y}^x
Cx+yx那么最终答案就是
C
t
x
+
y
×
C
x
+
y
x
C_{t}^{x+y}×C_{x+y}^x
Ctx+y×Cx+yx
预处理阶乘和逆元即可
O
(
1
)
O(1)
O(1)得到每个位置的答案
我看不懂的官方证明转化
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N=5010;
const ll mod=998244353;
ll fact[N],infact[N];
ll qmi(ll a,ll b,ll p)
{
ll res=1;
while(b)
{
if(b&1) res=res*a%p;
b>>=1;
a=a*a%p;
}
return res;
}
void init()
{
fact[0]=infact[0]=1;
for(int i=1;i<N;i++)
{
fact[i]=fact[i-1]*i%mod;
infact[i]=qmi(fact[i],mod-2,mod);
}
}
int n;
int main()
{
IO;
int T=1;
//cin>>T;
init();
while(T--)
{
cin>>n;
while(n--)
{
int x,y,t;
cin>>x>>y>>t;
if(x+y>t) cout<<0<<'\n';
else
{
ll res=1;
res=res*fact[t]*infact[x+y]%mod*infact[t-x-y]%mod;
res=res*fact[x+y]%mod*infact[x]%mod*infact[y]%mod;
cout<<res<<'\n';
}
}
}
return 0;
}
C.牛牛染颜色
树形dp
状态表示:
f
(
i
,
0
/
1
)
f_{(i,0/1)}
f(i,0/1)表示选择/不选择
u
u
u 这个节点后以
u
u
u 为根的子树的合法方案数。
状态转移:
若选择
u
u
u 这个节点,则子树内可以随便选点,每个子树独立乘法原理可得转移
f
(
i
,
1
)
=
∏
j
∈
s
o
n
f
(
j
,
0
)
+
f
(
j
,
1
)
f_{(i,1)}=\prod_{j\in son} f_{(j,0)}+f_{(j,1)}
f(i,1)=∏j∈sonf(j,0)+f(j,1)
若不选择
u
u
u 这个节点,则最多选择某一个子树,由加法原理可得转移
f
(
i
,
0
)
=
1
+
∑
j
∈
s
o
n
(
f
(
j
,
0
)
+
f
(
j
,
1
)
−
1
)
f_{(i,0)}=1+\sum_{j\in son}(f_{(j,0)}+f_{(j,1)}-1)
f(i,0)=1+∑j∈son(f(j,0)+f(j,1)−1)(
f
(
i
,
0
)
f_{(i,0)}
f(i,0)包含空集的方案,所以在枚举子树统计的时候每颗子树贡献的答案要减
1
1
1,但最后也要把空集的情况算上,还要加个
1
1
1)
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1000010,mod=1e9+7;
int h[N],e[2*N],ne[2*N],idx;
ll f[N][2];
int n;
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u,int fa)
{
f[u][0]=f[u][1]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==fa) continue;
dfs(j,u);
f[u][1]=(f[u][1]*(f[j][0]+f[j][1]))%mod;
f[u][0]=(f[u][0]+f[j][1]+f[j][0]-1)%mod;
}
}
int main()
{
IO;
int T=1;
//cin>>T;
while(T--)
{
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++)
{
int a,b;
cin>>a>>b;
add(a,b),add(b,a);
}
dfs(1,-1);
cout<<(f[1][0]+f[1][1])%mod<<'\n';
}
return 0;
}
D. 牛牛的呱数
对于大数,基本上都是取模达到我们想要的目的。
由此可以把原串取模后的答案记录下来,并且记录它的长度(边权),和别的串相接的过程就类似从一个状态到另一个状态,只需要预处理
1
0
k
%
p
10^k\%p
10k%p的结果跑最短路即可。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
const int N=210;
int ten[1000010];
struct node
{
int val,len;
}a[N];
int dist[N],n,p;
bool st[N];
void dijkstra()
{
memset(dist,0x3f,sizeof dist);
priority_queue<pii,vector<pii>,greater<pii> >q;
for(int i=1;i<=n;i++)
{
dist[a[i].val]=min(dist[a[i].val],a[i].len);
q.push({a[i].len,a[i].val});
}
while(q.size())
{
int t=q.top().second;q.pop();
if(st[t]) continue;
st[t]=1;
for(int i=1;i<=n;i++)
{
int now=(t*ten[a[i].len]%p+a[i].val)%p;
if(dist[now]>dist[t]+a[i].len)
{
dist[now]=dist[t]+a[i].len;
q.push({dist[now],now});
}
}
}
}
int main()
{
IO;
int T=1;
//cin>>T;
while(T--)
{
cin>>n>>p;
ten[0]=1;
for(int i=1;i<=1000000;i++) ten[i]=ten[i-1]*10%p;
for(int i=1;i<=n;i++)
{
string s;
cin>>s;
a[i].len=s.size();
reverse(s.begin(),s.end());
ll base=1;
ll now=0;
for(auto t:s)
{
now=(now+base*(t-'0')%p)%p;
base=base*10%p;
}
a[i].val=now%p;
}
dijkstra();
if(dist[0]==0x3f3f3f3f) cout<<"-1\n";
else cout<<dist[0]<<'\n';
}
return 0;
}
要加油哦~~