A、牛妹的游戏
题目描述:
在二维空间上有若干个点,有两队(蓝方和绿方),每队都可以占边。
而当有其中一队占的边有可能有三条首尾相连就输出"yes",否则输出"no"。
思路:
1.拉姆塞结论--点数超过5的图或者对应补图必有度数为3的环.不会证明(只会举例子)
2.由1知当n大于5时输出“yes”,只要判断n<=5的情况,数据较小,可以直接爆搜,任取三条边判断是否成环。
3.成环的依据就是如果三条边都别蓝方占领,那么就会形成蓝色三角形,如果都没被蓝方占领,那么就有可能被绿方占领,形成绿边。
Code:
#include<bits/stdc++.h>
using namespace std;
template <class T>
inline void read(T &res) {
char c; T flag = 1;
while ((c = getchar()) < '0' || c > '9')
if (c == '-')
flag = -1;
res = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
res = res * 10 + c - '0';
res *= flag;
}
inline void noread() {
char c;
while ((c = getchar()) < '0' || c > '9');
while ((c = getchar()) >= '0' && c <= '9');
}
int t,n,m,x,y;
int edge[10][10];
int main() {
read(t);
while(t--) {
read(n),read(m);
if(n>5) {
for(int i=0;i<m;++i) noread(),noread();
puts("yes");
continue;
}
memset(edge,0,sizeof edge);
for(int i=0;i<m;++i) {
read(x),read(y);
edge[x][y]=edge[y][x]=1;
}
bool flag=true;
for(int i=1;i<=n&&flag;++i)
for(int j=i+1;j<=n&&flag;++j)
for(int k=j+1;k<=n&&flag;++k) if(edge[i][j]==edge[j][k]&&edge[j][k]==edge[k][i])
flag=false;
if(flag) puts("no");
else puts("yes");
}
} B、病毒扩散
题目描述:
一个二维平面,从左下角开始病毒扩散,每一个时刻每个感染点可以传上右两个点,使其+1。
求某一点在某一时刻的感染数。
思路:
列举前4秒的表找规律(关键是当时我表都写不出来,虽然江大佬给我们看了表也说是杨辉三角,但是当时没感觉(江大佬tql))
第1秒: 第二秒:1x1
1 2x1 1x2
1 1 1x1 1x2 1x1
第三秒: 第四秒:1x1
1x1 4x1 1x4
3x1 1x3 6x1 3x4 1x6
3x1 2x3 1x3 4x1 3x4 2x6 1x4
1x1 1x3 1x3 1x1 1x1 1x4 1x6 1x4 1x1
观察 'x' 左边的数字,正确的阅读方式是从右往左看,你会发现这是个杨辉三角,比赛时江大佬一直说是杨辉三角,可能我太菜了没懂大佬意思,所以 'x' 左边的数字为 (x,y都是从0开始)。
然后 'x' 右边的数字就更简单了,列数相同 'x' 右边的数字就相同,每一列 'x' 右边的数字等于杨辉三角第t层第y列的数字,也就是 。
快速求组合数:知道公式 后,先预处理求出1 ~ n的阶乘
,再等需要的时候利用公式求直接求出,因为在做除法时是不能上下取余的,所以要改除一个数为乘一个数的逆元,可以用扩展欧几里得求逆元或者快速幂求逆元。快速幂求逆元码量小,如果a、mod互质,且mod为质数,那么a的逆元就是 。
Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
ll fact[100005];
template <class T>
inline void read(T &res) {
char c; T flag = 1;
while ((c = getchar()) < '0' || c > '9')
if (c == '-')
flag = -1;
res = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
res = res * 10 + c - '0';
res *= flag;
}
ll qpow(ll a,ll b) {
ll ans=1;
while(b) {
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
ll n,x,y,t;
int main() {
fact[0]=fact[1]=1;
for(int i=2;i<=100000;++i) fact[i]=fact[i-1]*i%mod;
read(n);
while(n--) {
read(x),read(y),read(t);
if(x+y>t) {
puts("0");
continue;
}
ll l=fact[t-y]*qpow(fact[x]*fact[t-x-y]%mod,mod-2)%mod;
ll r=fact[t]*qpow(fact[y]*fact[t-y]%mod,mod-2)%mod;
printf("%lld\n",l*r%mod);
}
} C、牛牛染颜色
题意:
牛牛最近得到了一颗树,根是 1 号节点,他想要把这颗树染色。
每个节点可以染成白色和黑色,牛牛认为一种染色方案是好的当且仅当任意两个黑点的 lca(最近公共祖先)的颜色也是黑色的。
求一共有多少种好的染色的方案。
答案可能很大,请输出答案对1e9 + 7取模后的结果。
思路:
知道题意后不难发现这是一道树形dp题(但是我不会),dp[u][0/1],0表示这个点涂黑色,1表示这个点涂白色。
分开来转移:
1.结点u涂黑,那无论子树是什么情况,尽管下面没有黑色都都是符合的,所以我们可以无所顾及地得到状态转移方程dp[u][0]= .
2.结点u涂白,所以我们单独把每个子节点是(黑/白)的情况加起来就好了,否则在 u 的子树内会存在两个点 x,y 使得 lca(x,y)=u,这样就不符合条件了。这个状态是包含空集的,所以在枚举子树统计的时候每颗子树贡献的答案要减 1,但最后也要把空集的情况算上,还要加个 1。
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 1e9 + 7;
const int maxn=1e6+7;
template <class T>
inline void read(T &res) {
char c; T flag = 1;
while ((c = getchar()) < '0' || c > '9')
if (c == '-')
flag = -1;
res = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
res = res * 10 + c - '0';
res *= flag;
}
int head[maxn],Next[maxn<<1],to[maxn<<1],tot;
ll dp[maxn][2];
void add(int x,int y) {
to[++tot]=y;
Next[tot]=head[x];
head[x]=tot;
}
void dfs(int x,int fa) {
ll zero=1,one=1;
for(int i=head[x];i;i=Next[i]) {
int y=to[i];
if(y==fa) continue;
dfs(y,x);
zero=zero*(dp[y][0]+dp[y][1])%mod;
one=(one+dp[y][0]+dp[y][1]-1)%mod;
}
dp[x][0] = zero; dp[x][1] = one;
}
int n,u,v;
int main() {
read(n);
for(int i=2;i<=n;++i) {
read(u),read(v);
add(u,v);
add(v,u);
}
dfs(1,0);
printf("%lld\n",(dp[1][0]+dp[1][1])%mod);
return 0;
} 
京公网安备 11010502036488号