海亮 7.12 dp专题1
海亮 7.12 dp专题1
P6280 [USACO20OPEN] Exercise G
我们可以发现 一个排列是由很多循环节构成的 那么这些循环节的长度取
问题转化为对于
结论:根据算术唯一分解定理
证明:假设存在一个
那么我们设置
状态转移即为
显然
那么最后的答案就是
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4 + 5;
inline int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int f[N] , n , mod;//前i个质数 和为j的lcm和
int isntprime[N] , prime[N] , cnt , ans;
signed main ()
{
ios::sync_with_stdio(false);
n = read() , mod = read();
for ( int i = 2 ; i <= n ; i ++ )
{
if ( !isntprime[i] ) prime[++cnt] = i;
for ( int j = 1 ; j <= cnt && i * prime[j] <= n ; j ++ )
{
isntprime[prime[j]*i] = 1;
if ( i % prime[j] == 0 ) break;
}
}
f[0] = 1;//和为0的时候也有一种情况 为1-n的排列
for ( int i = 1 ; i <= cnt ; i ++ )
for ( int j = n ; j >= prime[i] ; j -- )
{
int k = prime[i];
while ( k <= j ) f[j] = ( f[j] + ( f[j-k] * k ) ) % mod , k *= prime[i];
}
for ( int i = 0 ; i <= n ; i ++ ) ans = ( ans + f[i] ) % mod;
cout << ans;
return 0;
}
Karen and Supermarket
一个常规思路是设置
不过这里的
所以令
那么转移
相当于
注意
#include <bits/stdc++.h>
using namespace std;
const int N = 5000 + 5;
inline int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , val[N] , dis[N] , sz[N] , f[N][N][2];
int head[N] , cnt;
struct DQY { int to , nxt; } e[N<<1];
void add ( int u , int v ) { e[++cnt] = { v , head[u] }; head[u] = cnt; }
void dfs ( int u )
{
sz[u] = 1;
f[u][0][0] = 0;
f[u][1][0] = val[u];
f[u][1][1] = val[u] - dis[u];
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
dfs(v);
for ( int j = sz[u] ; j >= 0 ; j -- )
for ( int k = sz[v] ; k >= 0 ; k -- )
{
f[u][j+k][0] = min ( f[u][j+k][0] , f[u][j][0] + f[v][k][0] );
f[u][j+k][1] = min ( f[u][j+k][1] , f[u][j][1] + min ( f[v][k][1] , f[v][k][0] ) );
}
sz[u] += sz[v];
}
}
signed main ()
{
ios::sync_with_stdio(false);
n = read() , m = read();
val[1] = read() , dis[1] = read();
for ( int i = 2 , fa ; i <= n ; i ++ ) val[i] = read() , dis[i] = read() , fa = read() , add ( fa , i );
memset ( f , 0x3f , sizeof f );
dfs(1);
for ( int i = n ; i ; i -- )
if ( f[1][i][0] <= m || f[1][i][1] <= m )
{ cout << i << endl; return 0; }
cout << 0 << endl;
return 0;
}
P6147 [USACO20FEB] Delegation G
我们可以发现 经过点
那么显然对于某个点
那么我们可以对于每一个点
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
inline int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n;
int head[N] , cnt;
struct DQY { int to , nxt; } e[N<<1];
void add ( int u , int v ) { e[++cnt] = { v , head[u] }; head[u] = cnt; }
int dfs ( int u , int f , int k )
{
multiset<int>s;
for ( int i = head[u] , v ; v = e[i].to , i ; i = e[i].nxt )
{
if ( f == v ) continue;
int res = dfs ( v , u , k );
if ( res == -1 ) return -1;
if ( res == k ) continue;
if ( s.count(k-res) ) s.erase(s.find(k-res));
else s.insert(res);
}
if ( s.size() == 0 ) return 1;
if ( s.size() == 1 ) return *s.begin() + 1;
return -1;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read();
for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
for ( int i = 1 ; i < n ; i ++ )
{
if ( ( n - 1 ) % i ) cout.put('0');//只有可以被整除的才有机会
else
{
if ( dfs ( 1 , 0 , i ) == 1 ) cout.put('1');
else cout.put('0');
}
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
inline int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , f[N] , sz[N];
int head[N] , cnt;
struct DQY { int to , nxt; } e[N<<1];
void add ( int u , int v ) { e[++cnt] = { v , head[u] }; head[u] = cnt; }
int dfs ( int u , int f , int k )
{
map<int,int> mp;
sz[u] ++; //代表的是这个节点的剩余链大小(也可能没有剩余)
for ( int i = head[u] , v ; v = e[i].to , i ; i = e[i].nxt )
{
if ( f == v ) continue;
if ( ! dfs ( v , u , k ) ) return 0;
int match = k - sz[v];
if ( mp.count(match) )
{
mp[match] -- , sz[u] -= match;
if ( mp[match] == 0 ) mp.erase(match);//如果没有值了 那么将这个值清理掉
}
else if ( k != sz[v] ) sz[u] += sz[v] , mp[sz[v]] ++;//这棵子树的链加入失配队列
}
int rem = 0;
for ( auto it : mp ) rem += it.second;
return rem <= 1;//如果不能配对的小于一条 那么合法
}
signed main ()
{
ios::sync_with_stdio(false);
n = read();
for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
for ( int i = 1 ; i < n ; i ++ )
{
if ( ( n - 1 ) % i ) cout << 0;//只有可以被整除的才有机会
else
{
for ( int i = 1 ; i <= n ; i ++ ) sz[i] = 0;
if ( dfs ( 1 , 0 , i ) ) cout << 1;
else cout << 0;
}
}
return 0;
}
P3959 [NOIP2017 提高组] 宝藏
正解是状压(虽然我不会) 但是可以暴搜
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid (l+r>>1)
const int N = 1e3 + 5;
const int inf = 1000000;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , cntnode[N] , lay[N][N] , mp[N][N] , ans = inf , use[N];//cntnode是这一层有几个点 lay[i][j]表示第i层第j个点是什么
void dfs ( int layer , int p , int cnt , int res )//当前进行到了第layer层 当前在讨论p节点 已经加入了cnt个节点 现在的价值是res
{
if ( res > ans ) return;//最优
if ( cntnode[layer-1] == 0 ) return;
if ( cnt == n ) return ans = min ( ans , res ) , void();
if ( p > n ) return dfs ( layer + 1 , 1 , cnt , res ) , void();//下一层
if ( use[p] == 1 ) return dfs ( layer , p + 1 , cnt , res ) , void();//这个点被访问过了 跳过
use[p] = 1; int minn = inf;
for ( int i = 1 ; i <= cntnode[layer-1] ; i ++ ) minn = min ( minn , mp[p][lay[layer-1][i]] );//减一!!!!!!!!!!!!
use[p] = 1;
lay[layer][++cntnode[layer]] = p;
dfs ( layer , p + 1 , cnt + 1 , res + minn * layer );
--cntnode[layer];
use[p] = 0;
dfs ( layer , p + 1 , cnt , res );
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
memset ( mp , 0x3f , sizeof mp );
for ( int i = 1 , u , v , w ; i <= m ; i ++ ) { u = read() , v = read() , w = read(); if ( w < mp[u][v] ) mp[u][v] = mp[v][u] = w; }
for ( int i = 1 ; i <= n ; i ++ )
{
use[i] = 1;
lay[0][++cntnode[0]] = i;
dfs ( 1 , 1 , 1 , 0 );
--cntnode[0];
use[i] = 0;
}
cout << ans << endl;
return 0;
}
Tree Elimination
因为删除的时候边有先后顺序,这题
状态设置(
转移:
对于
1、
2、
有
注:上式中对于
对于
1、所有
2、
得
对于
这里的
即
那么我们做一个
注意乘的时候需要时刻取模!!!
(其实这是一篇
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 2e5 + 5;
const int mod = 998244353;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int f[N][4] , n , m , k;
int head[N] , cnt;
struct node { int to , nxt; } e[N<<1];
void add ( int u , int v ) { e[++cnt] = { v , head[u] }; head[u] = cnt; }
int a[N] , b[N] , c[N];
void dfs ( int u , int fa )
{
vector<int> vv;
for ( int i = head[u] , v ; v = e[i].to , i ; i = e[i].nxt )
{
vv.push_back(v);
if ( v != fa ) dfs ( v , u );
}
m = k = 0;
for ( int i = vv.size() - 1 ; i >= 0 ; i -- )
{
int v = vv[i];
if ( v == fa ) k = m;
else m ++ , a[m] = ( f[v][0] + f[v][1] ) % mod , b[m] = ( f[v][2] + f[v][3] ) % mod , c[m] = ( f[v][0] + f[v][2] + f[v][3] ) % mod;
}
a[0] = 1 , c[m+1] = 1;
for ( int i = m ; i ; i -- ) c[i] = c[i] * c[i+1] % mod;
for ( int i = 1 ; i <= m ; i ++ ) a[i] = a[i] * a[i-1] % mod;
for ( int i = 1 ; i <= m ; i ++ ) f[u][(i>k)<<1] = ( f[u][(i>k)<<1] + a[i-1] * b[i] % mod * c[i+1] % mod ) % mod;//内部三个东西的乘积也需要时刻取模!!!
f[u][1] = ( a[k] * c[k+1] ) % mod , f[u][3] = a[m] % mod;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read();
for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
dfs ( 1 , 0 );
cout << ( f[1][0] + f[1][2] + f[1][3] ) % mod << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)