JZOJ 4252 QYQ的图(深搜)
题目
给你一个
n
n
n个点,
m
m
m条边的无向图,每个点有一个非负的权值
c
i
ci
ci,现在你需要选择一些点,使得每一个点都满足:
如果这个点没有被选择,则与它有边相连的所有点都必须被选择。
问:满足上述条件的点集中,所有选择的点的权值和最小是多少?
分析
深搜
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int n,m,a[51],ans; long long spe[51];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void dfs(int dep,int sum,long long now){
if (sum>=ans) return;
if (dep>n){
ans=sum;
return;
}
if (!(now&(1ll<<dep))&&!(spe[dep]&(1ll<<dep))) dfs(dep+1,sum,now|spe[dep]);
dfs(dep+1,sum+a[dep],now|(1ll<<dep));
}
signed main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
n=iut(); m=iut();
for (rr int i=1;i<=n;++i) ans+=(a[i]=iut());
for (rr int i=1;i<=m;++i){
rr int x=iut(),y=iut();
spe[x]|=1ll<<y,spe[y]|=1ll<<x;
}
dfs(1,0,0);
return !printf("%d",ans);
}
JZOJ 4248 n染色
题目
给一个 n n n边形涂色,共有 m m m种颜色的笔,使相邻两边颜色不同的方案有多少
分析
设答案为 f [ n ] f[n] f[n],那么分类讨论新加进来的第 n n n条边
- 当两条邻边相同时,那么等同于在 n − 2 n-2 n−2条边内加入一条相同的边和一条不同的边,那也就是 f [ n − 2 ] × ( m − 1 ) f[n-2]\times(m-1) f[n−2]×(m−1)
- 当两条邻边不同时,也就是在 n − 1 n-1 n−1条边内加入一条与两条邻边都不同的边,那也就是 f [ n − 1 ] × ( m − 2 ) f[n-1]\times (m-2) f[n−1]×(m−2)
综上所述,
f
[
n
]
=
f
[
n
−
2
]
×
(
m
−
1
)
+
f
[
n
−
1
]
×
(
m
−
2
)
f[n]=f[n-2]\times(m-1)+f[n-1]\times(m-2)
f[n]=f[n−2]×(m−1)+f[n−1]×(m−2)
那考虑优化,首先可以矩阵乘法~~,但是这道题不需要~~
那可以简化这条式子
f
[
n
]
+
f
[
n
−
1
]
=
f
[
n
−
1
]
×
(
m
−
2
)
+
f
[
n
−
2
]
×
(
m
−
1
)
+
f
[
n
−
1
]
=
(
f
[
n
−
1
]
+
f
[
n
−
2
]
)
×
(
m
−
1
)
f[n]+f[n-1]=f[n-1]\times(m-2)+f[n-2]\times(m-1)+f[n-1]=(f[n-1]+f[n-2])\times(m-1)
f[n]+f[n−1]=f[n−1]×(m−2)+f[n−2]×(m−1)+f[n−1]=(f[n−1]+f[n−2])×(m−1)
那就可以得到
f
[
n
]
+
f
[
n
−
1
]
=
(
m
−
1
)
n
m
f[n]+f[n-1]=(m-1)^nm
f[n]+f[n−1]=(m−1)nm
那
(
f
[
n
]
+
f
[
n
−
1
]
)
−
(
f
[
n
−
1
]
+
f
[
n
−
2
]
)
±
⋯
±
(
f
[
1
]
−
f
[
0
]
=
m
)
=
f
[
n
]
(f[n]+f[n-1])-(f[n-1]+f[n-2])\pm\dots\pm(f[1]-f[0]=m)=f[n]
(f[n]+f[n−1])−(f[n−1]+f[n−2])±⋯±(f[1]−f[0]=m)=f[n]
解开后用等比数列求和即可
代码
#include <cstdio>
#define rr register
using namespace std;
typedef long long ll; const ll mod=1000000007;
ll ans,n,m;
inline ll ksm(ll x,ll y){
rr ll ans=1;
for (;y;y>>=1,x=(x*x)%mod)
if (y&1) ans=(ans*x)%mod;
return ans;
}
signed main(){
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
scanf("%lld%lld",&n,&m);
ans=(m-1)*((n&1)?-1:1)+ksm(m-1,n%(mod-1));
return !printf("%lld",ans%mod);
}
JZOJ 4249 游戏(贪心)
题目
从原点往正方向跳,若从 j j j跳到 i i i那么可以获得 a [ i ] × ( i − j ) a[i]\times(i-j) a[i]×(i−j)的分数,问如何使分数最大
分析
那么这道题可能是题目出的比较玄学,本来好像要斜率优化的,现在只需要维护后缀最大值贪心求解
代码
#include <cstdio>
#include <cctype>
#define rr register
#define max(a,b) ((a>b)?(a):(b))
using namespace std;
int n,a[100001],mx[100001],ans;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
signed main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
n=iut(); for (rr int i=1;i<=n;++i) a[i]=iut();
mx[n]=a[n]; rr int ls=0;
for (rr int i=n-1;i;--i) mx[i]=max(mx[i+1],a[i]);
for (rr int i=1;i<=n;++i)
if (a[i]==mx[ls+1])
ans+=(i-ls)*a[i],ls=i;
return !printf("%d",ans);
}