条边以及任意个点,要求其能够构造出最多的鸡爪,并且输出所有可能的构造中,边集顶点行优先遍历字典序最小的一个。
明眼易得最多构造
那么就可以让每个鸡爪的中心点和
那么对于
所以对其和顶点
此时编号为
编号为
剩下的,和编号
分类讨论即可,特殊处理鸡爪数量小于等于
#include<bits/stdc++.h>
#define int long long
#define endl '\n'//交互题就删
using namespace std;
const int inf=0x3f3f3f3f;
const int N=2e5+10;
int n,p;
void solve(){
>>n;p=n/3;
cinif(n==1)cout<<1<<" "<<2<<endl;
else if(n==2) {
<<1<<" "<<2<<endl;
cout<<1<<" "<<3<<endl;
cout}
else{
for(int i=2;i<=p;i++)cout<<1<<" "<<i<<endl;
<<1<<" "<<p+1<<endl;
cout<<1<<" "<<p+2<<endl;
cout<<1<<" "<<p+3<<endl;
coutfor(int i=3*p+1;i<=n;i++)cout<<1<<" "<<p+3+(i-3*p)<<endl;
for(int i=3;i<=p;i++)cout<<2<<" "<<i<<endl;
if(p>=2){
<<2<<" "<<p+1<<endl;
cout<<2<<" "<<p+2<<endl;
cout}
for(int i=4;i<=p;i++)cout<<3<<" "<<i<<endl;
if(p>=3)cout<<3<<" "<<p+1<<endl;}
//cout<<endl;
}
signed main(){
::sync_with_stdio(0);cin.tie(0);cout.tie(0);
iosint t=1;
>>t;
cinwhile(t--){solve();}
}
给定一个三阶魔方,保证只扭侧面不超过三次。扭的时候掉了同一个角的两篇贴纸,贴纸现在被胡乱的贴了回去。给出现在模仿状态的展开图,问贴对了贴纸没。
赛时觉得是硬暴力,码了五百多行(只能说还诶看情况听话)
旋转魔方的时候,魔方的八个角的贴纸一定遵循原先的拓补序,也就是说,无论怎么转,其侧面贴纸颜色的顺序一定不会发生改变,其指定视角下贴纸顺序固定。
所以,我们只需要暴力判断八个角的现行贴纸顺序对不对就行了。
有一个细节在于旋转之后可能发生循环位移(即原顺时针视角为123,现在顺时针视角为312),需要归一化。(上层归1,下层归6)
#include <bits/stdc++.h>
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define ll long long
#define db double
#define pii pair<int, int>
#define tii tuple<int, int, int>
#define i128 __int128
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
#define endl '\n'
using namespace std;
// #define int long long
// 原始魔方八个角,顺时针
struct cor
{
int a, b, c;
(int _a, int _b, int _c) : a(_a), b(_b), c(_c) {}
corbool operator==(cor t) const
{
return a == t.a && b == t.b && c == t.c;
}
void srt()
{ // 按大小排序
if (a > b)
(a, b);
swapif (b > c)
(b, c);
swapif (a > b)
(a, b);
swap}
void mkstd()
{ // 把三元组化为以1或6开头的形式
int t;
while (a != 1 && a != 6)
{
= a;
t = b;
a = c;
b = t;
c }
}
};
<cor> std_cor = <!--swig0-->; // 还原状态的8个角(逆时针)
vectorvoid solve()
{
<string> mf(9);
vectorfor (auto &s : mf)
>> s; // 注意加&
cin <cor> corners = {// 给定魔方的八个角,逆时针
vector{mf[0][3] - '0', mf[3][11] - '0', mf[3][0] - '0'},
{mf[8][3] - '0', mf[5][0] - '0', mf[5][11] - '0'},
{mf[3][8] - '0', mf[3][9] - '0', mf[0][5] - '0'},
{mf[5][8] - '0', mf[8][5] - '0', mf[5][9] - '0'},
{mf[6][3] - '0', mf[5][3] - '0', mf[5][2] - '0'},
{mf[3][3] - '0', mf[2][3] - '0', mf[3][2] - '0'},
{mf[6][5] - '0', mf[5][6] - '0', mf[5][5] - '0'},
{mf[3][6] - '0', mf[2][5] - '0', mf[3][5] - '0'}};
for (auto &cor : corners)
.mkstd();
corfor (auto &cor : corners)
{
int fl = 0;
for (auto &st : std_cor)
if (st == cor)
{
= 1;
fl break;
}
if (fl)
continue;
.srt();
cor<< cor.a << ' ' << cor.b << ' ' << cor.c << endl;
cout return;
}
<< "No problem" << endl;
cout }
signed main()
{
::sync_with_stdio(false);
ios.tie(0);
cin.tie(0);
coutint T;
>> T;
cin while (T--)
{
();
solve}
return 0;
}
给定一张图,将其复制
并查集维护多图联通状态,关键是如何维护计数。
将每个点在每张图中的祖先组成一个序列串,对序列字符串进行
考虑计数变化,如果对于第
// 1012 图计数(d张图,每次在某个图加入一条边,并询问有多少个无序点对在每张图上都连通)做法:哈希+启发式合并
// 维护d个并查集,这样每个点在每张图上的祖先组成了一个d长的序列,我们对序列哈希,如果两个序列相同就代表在每张图上都连通
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e4 + 10, mod = 998244353;
int d, n, m, k, ans;
int w[201][50005], fa[201][50005];
int weight[50005];
<int, int> b;
unordered_map<int> v[201][50001];
vectorint find(int k, int x)
{
if (fa[k][x] != x)
{
[k][x] = find(k, fa[k][x]);
fareturn fa[k][x];
}
else
return x;
}
inline void read(int &x)
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
= -1;
w = getchar();
ch }
while (ch >= '0' && ch <= '9')
{
= (s * 10) + ch - '0';
s = getchar();
ch }
= s * w;
x }
void add(int x, int y, int k, int c)
{ // 启发式合并
int fx = find(k, x), fy = find(k, y);
if (fx == fy)
{
if (c == 1)
<< ans << "\n";
cout return;
}
if (v[k][fx].size() > v[k][fy].size())
(fx, fy);
swap[k][fx] = fy;
fafor (int i : v[k][fx])
{
[weight[i]]--;
b-= b[weight[i]];
ans [i] -= w[k][fx];
weight[i] += w[k][fy];
weight+= b[weight[i]];
ans [weight[i]]++;
b[k][fy].push_back(i);
v}
[k][fx].clear();
vif (c == 1)
<< ans << "\n";
cout }
void solve()
{
= 0;
ans (time(0));
mt19937 myrand(n);
read(m);
read(d);
read(k);
read++;
dfor (int j = 1; j <= n; j++)
[j] = 0;
weightfor (int i = 1; i <= d; i++)
for (int j = 1; j <= n; j++)
[i][j].clear();
vfor (int i = 1; i <= d; i++)
{
for (int j = 1; j <= n; j++)
{
[i][j] = (long long)myrand() * (long long)myrand() % 1000000001339; // 每层图的每个点都赋一个随机点权
w[j] += w[i][j]; // 一个点的权重就是各层的相加
weight[i][j].push_back(j); // 存以i、j为父亲的所有点
v[i][j] = j; // 记录父亲
fa}
}
.clear();
bfor (int i = 1; i <= n; i++)
[weight[i]]++; // 赋初值
bfor (int i = 1; i <= m; i++)
{
int x, y;
(x);
read(y);
readfor (int j = 1; j <= d; j++)
{
(x, y, j, 0);
add}
}
for (int i = 1; i <= k; i++)
{
int x, y, w;
(x);
read(y);
read(w);
read(x, y, w, 1);
add}
}
signed main()
{
int T;
(T);
readwhile (T--)
();
solvereturn 0;
}
给定一个初始字符串
板子题,但是不知道为何
前面离线模式串
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 200010;
#define maxn 200010
char s[maxn], T[maxn];
int n, cnt, vis[200051], ans, in[maxn], Map[maxn];
struct kkk
{
int son[26], fail, flag, ans;
void clear() { memset(son, 0, sizeof(son)), fail = flag = ans = 0; }
} trie[maxn];
<int> q;
queuevoid insert(char *s, int num)
{
int u = 1, len = strlen(s);
for (int i = 0; i < len; i++)
{
int v = s[i] - 'a';
if (!trie[u].son[v])
[u].son[v] = ++cnt;
trie= trie[u].son[v];
u }
if (!trie[u].flag)
[u].flag = num;
trie[num] = trie[u].flag;
Map}
void getFail()
{
for (int i = 0; i < 26; i++)
[0].son[i] = 1;
trie.push(1);
qwhile (!q.empty())
{
int u = q.front();
.pop();
qint Fail = trie[u].fail;
for (int i = 0; i < 26; i++)
{
int v = trie[u].son[i];
if (!v)
{
[u].son[i] = trie[Fail].son[i];
triecontinue;
}
[v].fail = trie[Fail].son[i];
trie[trie[v].fail]++;
in.push(v);
q}
}
}
void topu()
{
for (int i = 1; i <= cnt; i++)
if (in[i] == 0)
.push(i);
qwhile (!q.empty())
{
int u = q.front();
.pop();
q[trie[u].flag] = trie[u].ans;
visint v = trie[u].fail;
[v]--;
in[v].ans += trie[u].ans;
trieif (in[v] == 0)
.push(v);
q}
}
void query(char *s)
{
int u = 1, len = strlen(s);
for (int i = 0; i < len; i++)
= trie[u].son[s[i] - 'a'], trie[u].ans++;
u }
bool check[maxn];
char b[MAX_N], a[MAX_N];
int P[MAX_N]; // next数组,next[i]表示1-i的最长前缀后缀的那个长度
int nt, mt;
void KMP_start()
{
= strlen(b + 1);
mt [1] = 0;
Pfor (int i = 2, j = 0; i <= mt; i++)
{
while (j > 0 && b[i] != b[j + 1])
= P[j]; // p就是next数组,j在循环中最初表示的是next[i-1],这步是重复跳next
j if (b[j + 1] == b[i])
++; // 如果相等则next进步
j[i] = j; // 赋值next数组
P}
}
bool KMP_judge()
{
= strlen(a + 1);
nt for (int i = 1, j = 0; i <= nt; i++)
{
while (j > 0 && a[i] != b[j + 1])
= P[j];
j if (b[j + 1] == a[i])
++;
jif (j == mt)
{
return true;
}
}
return false;
}
char str[MAX_N + 10];
char ss[MAX_N + 10];
int main()
{
int t;
("%d", &t);
scanfwhile (t--)
{
int n;
("%d", &n);
scanf= 1;
cnt ("%s", str); // A
scanf("%s", b + 1); // 1-n存储C
scanf();
KMP_startfor (int i = 1; i <= n; i++)
{
bool flag = 1;
("%s", ss);
scanf("%s", a + 1);
scanf[i] = KMP_judge();
check(ss, i);
insert}
();
getFail(str);
query();
topufor (int i = 1; i <= n; i++)
{
if (check[i])
{
if (vis[Map[i]] > 0)
{
("%d", i);
printfif (i != n)
(" ");
printf}
}
}
("\n");
printffor (int i = 1; i <= cnt; i++)
[i].clear();
trie}
//fflush(stdout);
// system("pause");
return 0;
}
给一棵树,这棵树会不断的成长。对于一个节点
特别的,对于一个度数为0或1的点,进行成长将不会发生变化。
对于一棵树的成长,定义为树上所有的节点进行一次成长。求在这棵树进行
这里定义树的直径的长度为直径上的点数。
答案对
不难返现度数为
对于初始树的每一个节点,让其成长到
// 1008 图生长
// 注意到度数为i的点数成长m轮的贡献是确定的,2^(m-1)*结点为2的数量,2^m-1结点为3的数量。m太大时可以用1:2来估计
// 所以我们就以这个贡献跑树的直径即可,树形dp思想以每个点为根求最长路和次长路即可
// 但是m太大时我们计算不了每个点的贡献,就按照上面那个来计算,所以最后ans要存结点为2的数量和3的数量,最后再用具体的A、B来计算最终答案
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int n, m, A, B;
<int> e[N];
vectorstruct node
{
int x, y;
friend inline node operator+(const node &A, const node &B)
{
return (node){A.x + B.x, A.y + B.y};
}
} ans, f[N];
int sum(node a)
{
return 1LL * A * a.x + 1LL * B * a.y;
}
long long ksm(int x, int y)
{
if (y == 0)
return 1;
long long temp = ksm(x, y / 2) % mod;
if (y % 2 == 0)
return temp * temp % mod;
else
return (temp * temp % mod) * x % mod;
}
void dfs(int u, int fa)
{
if (e[u].size() >= 2)
[u] = (node){2, e[u].size() - 2}; // 每个点生长一次有几个2度点、3度点
felse
[u] = (node){0, 0};
f= (node){0, 0}, mmx = (node){0, 0};
node mx for (auto i : e[u])
{
if (i == fa)
continue;
(i, u);
dfsif (sum(f[i]) > sum(mx))
{
= mx, mx = f[i];
mmx }
else if (sum(f[i]) > sum(mmx))
= f[i];
mmx } // 找最大和次大的两个点往下走(类似树形dp)
if (sum(f[u]) + sum(mx) + sum(mmx) > sum(ans))
= f[u] + mx + mmx;
ans [u] = f[u] + mx;
f}
void solve()
{
>> n >> m;
cin for (int i = 1; i <= n; i++)
[i].clear();
efor (int i = 1; i < n; i++)
{
int u, v;
>> u >> v;
cin [u].push_back(v);
e[v].push_back(u);
e}
if (m <= 20)
= 1 << m - 1, B = (1 << m) - 1;
A else
= 1, B = 2; // 跑dfs时作为一个点的贡献的系数
A .x = 0;
ans.y = 0; // ans记录了最后有几个2度节点和3度节点
ans(1, 0);
dfs= ksm(2, m - 1);
A = (ksm(2, m) - 1 + mod) % mod;
B << ((ans.x * A % mod + ans.y * B % mod) + 2) % mod << endl;
cout ; // 放大,正常输出答案
}
signed main()
{
::sync_with_stdio(0);
ios.tie(0);
cin.tie(0);
coutint t = 1;
>> t;
cin while (t--)
{
();
solve}
}
定义一个有根树森林是深度自同构的,当且仅当其任意两个深度相同的结点都拥有一致的度数。
给定
保证
赛时数
不难发现其必然是一个对称的结构,考虑因数枚举。
设
设结果为
void init(int n)
{
for (int i = 2; i <= n; i++)
{
[i] += (f[i - 1] + 1);
ffor (int j = (2 * i); j <= n; j += i)
{
[j] += f[i - 1];
f}
}
}
所以可以轻松通过时间限制。
#include <bits/stdc++.h>
using namespace std;
template <int MOD> // 模板参数
struct modint
{
int val;
static int norm(const int &x) { return x < 0 ? x + MOD : x; }
static constexpr int get_mod() { return MOD; }
() const
modint inv{
assert(val);
int a = val, b = MOD, u = 1, v = 0, t;
while (b > 0)
= a / b, swap(a -= t * b, b), swap(u -= t * v, v);
t assert(b == 0);
return modint(u);
}
() : val(0) {}
modint(const int &m) : val(norm(m)) {}
modint(const long long &m) : val(norm(m % MOD)) {}
modintoperator-() const { return modint(norm(-val)); }
modint bool operator==(const modint &o) { return val == o.val; }
bool operator<(const modint &o) { return val < o.val; }
&operator+=(const modint &o) { return val = (1ll * val + o.val) % MOD, *this; }
modint &operator-=(const modint &o) { return val = norm(1ll * val - o.val), *this; }
modint &operator*=(const modint &o) { return val = static_cast<int>(1ll * val * o.val % MOD), *this; }
modint &operator/=(const modint &o) { return *this *= o.inv(); }
modint &operator^=(const modint &o) { return val ^= o.val, *this; }
modint &operator>>=(const modint &o) { return val >>= o.val, *this; }
modint &operator<<=(const modint &o) { return val <<= o.val, *this; }
modint operator-(const modint &o) const { return modint(*this) -= o; }
modint operator+(const modint &o) const { return modint(*this) += o; }
modint operator*(const modint &o) const { return modint(*this) *= o; }
modint operator/(const modint &o) const { return modint(*this) /= o; }
modint operator^(const modint &o) const { return modint(*this) ^= o; }
modint operator>>(const modint &o) const { return modint(*this) >>= o; }
modint operator<<(const modint &o) const { return modint(*this) <<= o; }
modint friend std::istream &operator>>(std::istream &is, modint &a)
{
long long v;
return is >> v, a.val = norm(v % MOD), is;
}
friend std::ostream &operator<<(std::ostream &os, const modint &a) { return os << a.val; }
friend std::string tostring(const modint &a) { return std::to_string(a.val); }
friend modint quickpow(const modint &a, const int &b)
{
assert(b >= 0);
= a, res = 1;
modint x for (int p = b; p; x *= x, p >>= 1LL)
if (p & 1)
*= x;
res return res;
}
};
using M107 = modint<1000000007>;
using M998 = modint<998244353>;
using Mint = M998;
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
<int> pri;
vectorconst int N = 1e6 + 10;
bool isnp[N]; // is not prime: 不是素数设为1,是素数设为0
[N];
Mint fvoid init(int n)
{
for (int i = 2; i <= n; i++)
{
[i] += (f[i - 1] + 1);//每个数都有1和自己作为因数,暴力转移
ffor (int j = (2 * i); j <= n; j += i)
{
[j] += f[i - 1];
f}
}
}
signed main()
{
;
IOSint n;
>> n;
cin [0] = f[1] = 1;
f(n);
initfor (int i = 1; i <= n; i++)
{
<< f[i] << " ";
cout }
}
给定一张
除了正常跑最短路之外要考虑幻影移形的问题。
首先不可否定的是,如果选择从两个奇数点幻影移形的代价一定不低于从
如果从偶数点向奇数点幻影移形的代价同样一定会劣于从偶数点向
但是,如果从偶数点向偶数点幻影移形的代价,不一定会劣于
考虑只特判偶数到偶数的幻影移形,将
枚举当前点的超集多取一个松弛操作就可以了。注意不要存边,否则会
#include <bits/stdc++.h>
#define int long long
// #define DEBUG 1
using namespace std;
typedef pair<int, int> P;
const int inf = 1e18;
const int N = 1e6 + 10;
int n, m, k, len;
int dis[N], vis[N];
struct node
{
int to, val;
};
<node> edge[N];
vectorvoid add(int x)
{
#ifdef DEBUG
<9> b(x);
bitset<< "now adding edges at x = " << x << endl;
cout << "bindary shows : " << b << endl;
cout << "len = " << len << endl;
cout #endif
<int> e;
vectorfor (int i = 1; i < len; i++)
{
if (((x >> i) & 1) == 0)
{
.push_back(1 << i);
e}
}
int s = e.size();
#ifdef DEBUG
<< "s = " << s << endl;
cout #endif
for (int i = 1; i < (1 << s); i++)
{
int sum = x;
for (int j = 0; j < s; j++)
{
if (((i >> j) & 1))
{
+= e[j];
sum }
}
#ifdef DEBUG
<< "sum = " << sum << endl;
cout << "bindary shows : " << bitset<9>(sum) << endl;
cout #endif
if (sum <= n)
// edge[x].push_back({sum, (sum)*k});
[sum] = min(dis[sum], dis[x] + sum * k);
dis}
}
void dijkstra()
{
[1] = 0;
dis<P, vector<P>, greater<P>> q;
priority_queue.push({0, 1});
qwhile (!q.empty())
{
= q.top();
P u .pop();
qif (vis[u.second])
continue;
[u.second] = 1;
visfor (auto j : edge[u.second])
{
if (dis[j.to] > dis[u.second] + j.val)
{
[j.to] = dis[u.second] + j.val;
dis.push({dis[j.to], j.to});
qif ((j.to) % 2 == 0 && dis[j.to] < k)
{ // 加边
(j.to);
add}
}
}
}
}
void solve()
{
>> n >> m >> k;
cin int t = n;
= 0;
len while (t)
{
>>= 1;
t ++;
len}
for (int i = 1; i <= n; i++)
[i].clear(), vis[i] = 0, dis[i] = inf;
edgefor (int i = 1, u, v, sum; i <= m; i++)
{
>> u >> v >> sum;
cin [u].push_back({v, sum});
edge[v].push_back({u, sum});
edge}
for (int i = 1; i <= n; i++)
[1].push_back({i, k * (1 | i)}); // 1连到i的边
edge();
dijkstrafor (int i = 2; i <= n; i++)
{
<< dis[i] << " ";
cout }
<< endl;
cout }
signed main()
{
#ifdef DEBUG
("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
freopen#endif
::sync_with_stdio(0);
ios.tie(0);
cin.tie(0);
coutint t = 1;
>> t;
cin while (t--)
{
();
solve}
}
维护一个数据结构,判断以下内容
对于一个整数数列,如果其先严格递增,然后在某一点后严格递减,我们称这个数列为单峰数列(严格递增和严格递减的部分均要是非空)。
给定长度为
1 l r x
:将 2 l r
:判断 3 l r
:判断 4 l r
:判断 5 l r
:判断 最唐氏的一集,直接开三个树状数组维护差分数组第
赛时单线段树就是维护不过来,思考区间
第五个操作维护二分就行了。
#include <bits/stdc++.h>
// #define DEBUG 1
using namespace std;
#define int long long
const int maxn = 1e5 + 10;
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
struct BIT
{
<int> c;
vectorint ns;
void init(int n)
{
.resize(n + 1, 0);
c= n;
ns }
int lowbit(int x)
{
return x & -x;
}
void add(int x, int y)
{
for (int i = x; i <= ns; i += lowbit(i))
[i] += y;
c}
int query(int x)
{
int ans = 0;
for (int i = x; i; i -= lowbit(i))
+= c[i];
ans return ans;
}
};
[3];
BIT bitint a[maxn];
int b[maxn];
int n;
void modifybit(int i, int x)
{
if (b[i] > 0)
[1].add(i, x);
bitif (b[i] < 0)
[2].add(i, x);
bitif (b[i] == 0)
[0].add(i, x);
bit}
void modify(int l, int r, int x)
{
(l, -1);
modifybit(r + 1, -1);
modifybit[l] += x;
b[r + 1] -= x;
b(l, 1);
modifybit(r + 1, 1);
modifybit}
bool checkeuqal(int l, int r)
{
return bit[0].query(r) - bit[0].query(l) == r - l;
}
bool checkraise(int l, int r)
{
return bit[1].query(r) - bit[1].query(l) == r - l;
}
bool checkfall(int l, int r)
{
return bit[2].query(r) - bit[2].query(l) == r - l;
}
bool checkup_down(int l, int r)
{
int cl = l, cr = r + 1;
while (cr - cl > 1)
{
#ifdef DEBUG
<< "cl=" << cl << " cr=" << cr << endl;
cout #endif
int mid = (cl + cr) >> 1LL;
if (checkraise(l, mid))
{
#ifdef DEBUG
<< "checkraise at " << l << " to " << mid << endl;
cout #endif
= mid;
cl }
else
{
#ifdef DEBUG
<< "not checkraise at " << l << " to " << mid << endl;
cout #endif
= mid;
cr }
}
if (cl == l)
return false;
if (checkfall(cl, r))
return true;
return false;
}
void solve()
{
>> n;
cin [0].init(n);
bit[1].init(n);
bit[2].init(n);
bit#ifdef DEBUG
<< "n=" << n << endl;
cout << "successfully create bit" << endl;
cout #endif
for (int i = 1; i <= n; i++)
{
>> a[i];
cin [i] = a[i] - a[i - 1];
b(i, 1);
modifybit}
#ifdef DEBUG
<< "successfully init bit" << endl;
cout for (int i = 1; i <= n; i++)
<< a[i] << " ";
cout << endl;
cout for (int i = 1; i <= n; i++)
<< b[i] << " ";
cout << endl;
cout for (int i = 1; i <= n; i++)
<< bit[0].query(i) - bit[0].query(i - 1) << " ";
cout << endl;
cout for (int i = 1; i <= n; i++)
<< bit[1].query(i) - bit[1].query(i - 1) << " ";
cout << endl;
cout for (int i = 1; i <= n; i++)
<< bit[2].query(i) - bit[2].query(i - 1) << " ";
cout << endl;
cout #endif
int q;
>> q;
cin while (q--)
{
int op, l, r, x;
>> op >> l >> r;
cin switch (op)
{
case 1:
>> x;
cin (l, r, x);
modifybreak;
case 2:
<< checkeuqal(l, r) << endl;
cout break;
case 3:
<< checkraise(l, r) << endl;
cout break;
case 4:
<< checkfall(l, r) << endl;
cout break;
case 5:
<< checkup_down(l, r) << endl;
cout }
}
}
signed main()
{
#ifdef DEBUG
("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
freopen#endif
;
IOSint t;
// cin >> t;
= 1;
t while (t--)
();
solvereturn 0;
}
给定
模拟,标答是三分,不会()
感觉也没必要三分,赛时唐在模拟的过于复杂,想记录的东西太多,不是打表题记那么多状态干什么?
思想很简单,因为每条最外框边界线的移动一定是一个一次函数,而且其状态只有四种:一直不变、先缩后扩、先平后扩、一直外扩。
换言之,单条边状态改变点至多只有小常数个(算上初始
所以只记录突变时间点就可以了,因为时间点很少,所有同学在该点的状态直接暴力枚举计算出当前外框坐标值就行了,别tm一直搁哪惦记着怎么维护当前时间具体是哪位同学在外面来
在考虑突变计算点的计算问题,初始时并不知道最靠外的
那就直接从同移动方向的人群的最值入手维护就行了,这个总能知道向某个方向移动的人的最值吧?
以向东走的为例。
向东走的只有三种状态改变可能:一直向东走没有遇到任何人、遇到一个南北走的
其他同理,把所有的全算了,取最小值,以时间成本换代码成本,下班。
#include <cstdio>
#include <iostream>
#include <set>
#define int long long
#define RI register int
#define CI const int &
using namespace std;
const int N = 200005, INF = 1e18;
const int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, -1, 1};
int n, x[N], y[N], mnx[4], mxx[4], mny[4], mxy[4]; // 0向东,1向西,2向南,3向北
char s[10], ch[N];
signed main()
{
;
RI ifor (scanf("%lld", &n), i = 1; i <= n; ++i)
("%lld%lld%s", &x[i], &y[i], s), ch[i] = s[0];
scanfauto trs = [&](const char &ch)
{
switch (ch)
{
case 'E':
return 0;
case 'W':
return 1;
case 'S':
return 2;
case 'N':
return 3;
default:
return -1;
}
};
for (i = 0; i < 4; ++i)
[i] = mny[i] = INF, mxx[i] = mxy[i] = -INF;
mnxfor (i = 1; i <= n; ++i)
{
int tp = trs(ch[i]);
// 向tp方向走的最值
[tp] = min(mnx[tp], x[i]);
mnx[tp] = max(mxx[tp], x[i]);
mxx[tp] = min(mny[tp], y[i]);
mny[tp] = max(mxy[tp], y[i]);
mxy}
<int> key;
set.insert(0);
keyif (mxx[0] != -INF)//有向东走的人
{
int mx = max(mxx[2], mxx[3]);//南北向在x轴的最大定值
if (mxx[0] < mx)//向东走的人在x轴的最大定值小于南北向在x轴的最大定值,可能超越其成为最大值
.insert(mx - mxx[0]);//记录时间点
key= mxx[1];//向西走的人在x轴最大定值
mx if (mxx[0] < mx)//东西向相向而行直到遇见后导致函数分段时间
.insert((mx - mxx[0]) / 2), key.insert((mx - mxx[0] + 1) / 2);//防止小数,多算两个
key}
if (mnx[1] != INF)
{
int mn = min(mnx[2], mnx[3]);
if (mnx[1] > mn)
.insert(mnx[1] - mn);
key= mnx[0];
mn if (mnx[1] > mn)
.insert((mnx[1] - mn) / 2), key.insert((mnx[1] - mn + 1) / 2);
key}
if (mny[2] != INF)
{
int mn = min(mny[0], mny[1]);
if (mny[2] > mn)
.insert(mny[2] - mn);
key= mny[3];
mn if (mny[2] > mn)
.insert((mny[2] - mn) / 2), key.insert((mny[2] - mn + 1) / 2);
key}
if (mxy[3] != INF)
{
int mx = max(mxy[0], mxy[1]);
if (mxy[3] < mx)
.insert(mx - mxy[3]);
key= mxy[2];
mx if (mxy[3] < mx)
.insert((mx - mxy[3]) / 2), key.insert((mx - mxy[3] + 1) / 2);
key}
int ans = INF;
for (auto t : key)
{
int res = 0, xl = INF, xr = -INF, yl = INF, yr = -INF;
for (i = 1; i <= n; ++i)
{
int tp = trs(ch[i]), nx = x[i] + t * dx[tp], ny = y[i] + t * dy[tp];
= min(xl, nx);
xl = max(xr, nx);
xr = min(yl, ny);
yl = max(yr, ny);
yr }
= min(ans, (xr - xl) + (yr - yl));
ans }
return printf("%lld", 2LL * ans), 0;
}
显然摆渡船的过河次数是固定的,考虑最后一发不归之途必定能载过去
这些次摆渡船必须需要
考虑枚举所有体力值大于等于
枚举人数拼凑,能凑够就能过去。
#include <bits/stdc++.h>
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define ll long long
#define db double
#define pii pair<int, int>
#define tii tuple<int, int, int>
#define i128 __int128
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
#define endl '\n'
using namespace std;
//#define int long long
const int maxn=5e5+10;
int h[maxn],a[maxn];
signed main() {
::sync_with_stdio(false);
ios.tie(0);
cin.tie(0);
coutint n,L,R;
>>n>>L>>R;
cinint k=(n-R+(R-L-1))/(R-L); //需要从河的右侧往回运送的趟数最小值,注意向上取整
int ans=0;
(i,1,n){
rep>>h[i];
cin[i]=(h[i]-1)/2; //每个人最多来回的趟数(最后还得过去)
a+=min(a[i],k);
ans}
if(ans>=k*L) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
两道辗转相减
一辆车做碰撞试验,一共有
取个
考虑使用
不难发现其一定可以变成一个辗转相减的过程,也就是说,其单位间隙必然是群体
秒了。
给定一个长度为
选择一个索引
对于所有
对于所有
例如,如果原始数组为
现在,你想通过这些操作最小化数组的范围。回想一下,数组的范围是数组中最大元素和最小元素之差。
显然最优的是折中翻。翻两次就会发现辗转相减的过程,然后就又变成
不同的是,这个
给定一个长度为
又是高等代数题。
排列可拆成不相交循环的乘积。
显然,如果某个不相交循环是一个对换,那么可以将两个对换组合起来。
如果是一个大小为
如果不相交循环大小大于等于
构造一个满足以下要求的树最少需要多少个节点?
显然构造一颗蒲公英菊花图(一个菊花图,中心点再接一条直链)是最优的,不难算出主链
这里要注意一个特殊判定,如果
一个
保证
分层图最长路
因为
对于每一个点,尝试十字架形态移动,如果路径有击败怪兽,就转向下一张状态图;否则在本层图中拓补
对于可能的环所造成的后效性拓补,采用最短路思想
时间复杂度:
#include <bits/stdc++.h>
using namespace std;
//#define DEBUG 1
#define cout std::cout
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n, m, k, sx, sy;
struct monster
{
int x, y, a, b, c;
} mon[15];
int maxd;
int graph[35][35];
const int INF = 0x3f3f3f3f;
int dp[35][35][(1 << 10) + 5];
int cost[35][35][(1 << 10) + 5];
int B;
int dist(int x1, int y1, int x2, int y2)
{
return abs(x1 - x2) + abs(y1 - y2);
}
int ans = 0;
int dir[4][2] = <!--swig1-->;
void Dijkstra_like_dp(int b)
{
#ifdef DEBUG
<< "now dp at level " << b << endl;
cout #endif
<tuple<int, int, int>> q;
priority_queuefor (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
if (dp[i][j][b] > -INF)
{
.push({dp[i][j][b], i, j});
q}
}
<vector<bool>> vis(n + 1, vector<bool>(m + 1, false));
vector#ifdef DEBUG
<< "q size is " << q.size() << endl;
cout #endif
while (!q.empty())
{
auto [dps, x, y] = q.top();
#ifdef DEBUG
<< "now at " << x << " " << y << " with dps " << dps << endl;
cout #endif
.pop();
qif (vis[x][y])
continue;
[x][y] = true;
vis= max(ans, dps);
ans for (int p = 0; p < 4; p++)
{
int status = 0, val = 0;
for (int d = 1; d <= maxd; d++)
{
int nowx = x + dir[p][0] * d, nowy = y + dir[p][1] * d;
if (nowx < 1 || nowx > n || nowy < 1 || nowy > m)
break;
#ifdef DEBUG
<< "now at " << nowx << " " << nowy << " with d " << d << endl;
cout #endif
if (graph[nowx][nowy] < k && !((b >> graph[nowx][nowy]) & 1)) // 路径上有怪兽并且未消灭
{
#ifdef DEBUG
<< "kill monster " << graph[nowx][nowy] << endl;
cout #endif
|= (1 << graph[nowx][nowy]);
status += mon[graph[nowx][nowy]].a;
val #ifdef DEBUG
<< "status afterkill " << bitset<10>(status) << endl;
cout << "val now " << val << endl;
cout << "nowpos used to have a monster,cannot stop" << endl;
cout #endif
}
else if (status == 0) // 路径上没怪兽,图层间最长路dp
{
#ifdef DEBUG
<< "up to now meets no monsters" << endl;
cout #endif
if (dp[nowx][nowy][b] < dps - cost[nowx][nowy][b])
{
[nowx][nowy][b] = dps - cost[nowx][nowy][b];
dp.push({dp[nowx][nowy][b], nowx, nowy});
q#ifdef DEBUG
<< "have good choices,update dp" << endl;
cout << "dp[" << nowx << "][" << nowy << "][" << (bitset<10>(b)) << "]=" << dp[nowx][nowy][b] << endl;
cout #endif
}
}
else // 路径上已经击杀目标,向下一层转移
{
#ifdef DEBUG
<< "nowpos don't to have a monster,can stop" << endl;
cout << "have killed monsters" << endl;
cout #endif
if (dp[nowx][nowy][b | status] < dps - cost[nowx][nowy][b | status] + val)
{
[nowx][nowy][b | status] = dps - cost[nowx][nowy][b | status] + val;
dp#ifdef DEBUG
<< "dp[" << nowx << "][" << nowy << "][" << (bitset<10>(b | status)) << "]=" << dp[nowx][nowy][b | status] << endl;
cout #endif
}
}
}
}
}
return;
}
void solve()
{
>> n >> m >> k;
cin = (1 << k);
B >> maxd;
cin for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
[i][j] = k + 1;
graph}
for (int i = 0; i < k; i++)
>> mon[i].x >> mon[i].y >> mon[i].a >> mon[i].b >> mon[i].c, graph[mon[i].x][mon[i].y] = i;
cin #ifdef DEBUG
<< "graph init" << endl;
cout for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
<< graph[i][j] << " ";
cout }
<< endl;
cout }
#endif
for (int i = 0; i < B; i++)
{
for (int j = 1; j <= n; j++)
{
for (int w = 1; w <= m; w++)
{
[j][w][i] = -INF;
dp[j][w][i] = 0;
costfor (int t = 0; t < k; t++)
{
if (((i >> t) & 1) == 0) // 未击杀
{
#ifdef DEBUG
<< "monster " << t << " not killed" << endl;
cout << "dist=" << dist(j, w, mon[t].x, mon[t].y) << endl;
cout #endif
if (dist(j, w, mon[t].x, mon[t].y) <= mon[t].c)
{
#ifdef DEBUG
//cout << "monster " << t << " add" << endl;
#endif
[j][w][i] += mon[t].b;
cost//cout << "now cost[" << j << "][" << w << "][" << (bitset<10>(i)) << "]=" << cost[j][w][i] << endl;
}
}
}
#ifdef DEBUG
<< "cost[" << j << "][" << w << "][" << (bitset<10>(i)) << "]=" << cost[j][w][i] << endl;
cout #endif
}
}
}
>> sx >> sy;
cin [sx][sy][0] = 0;
dp#ifdef DEBUG
<< "dp init" << endl;
cout << "pos is " << sx << " " << sy << endl;
cout #endif
= 0;
ans for (int i = 0; i < B; i++)
{
(i);
Dijkstra_like_dp}
<< ans << endl;
cout }
signed main()
{
//freopen("10021.in", "r", stdin);
//freopen("10021.out", "w", stdout);
;
IOSint t;
>> t;
cin while (t--)
();
solvereturn 0;
}
数据结构优化
有
考虑一个
注意到范围取
如果考虑到
然后遍历以
最后和
涉及单点修改和区间查询,把所有的
// 线段树优化dp方程转移
#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
const int maxn = 3e5 + 9;
struct node
{
int l, r;
int max = 0;
int lazy = 0;
};
[maxn << 2LL];
node treeint n, m;
int costs[maxn];
<pair<int, int>> mp[maxn];
vectorint f[maxn];
void pushup(int p)
{
[p].max = max(tree[p << 1LL].max, tree[p << 1LL | 1].max);
tree}
void pushdown(int p)
{
if (tree[p].lazy)
{
[p << 1LL].max += tree[p].lazy;
tree[p << 1LL].lazy += tree[p].lazy;
tree[p << 1LL | 1].max += tree[p].lazy;
tree[p << 1LL | 1].lazy += tree[p].lazy;
tree[p].lazy = 0;
tree}
}
void modify(int l, int r, int d, int p = 1, int cl = 0, int cr = n)
{
if (cl > cr || r < cl || cr < l)
return;
if (cl >= l && cr <= r)
{
[p].max += d;
tree[p].lazy += d;
treereturn;
}
(p);
pushdownint mid = (cl + cr) >> 1LL;
(l, r, d, p << 1LL, cl, mid);
modify(l, r, d, p << 1LL | 1, mid + 1, cr);
modify(p);
pushup}
int query(int l, int r, int p = 1, int cl = 0, int cr = n)
{
if (cl > cr || r < cl || cr < l)
return 0;
if (cl >= l && cr <= r)
return tree[p].max;
(p);
pushdownint mid = (cl + cr) >> 1LL;
return max(query(l, r, p << 1LL, cl, mid), query(l, r, p << 1LL | 1, mid + 1, cr));
}
signed main()
{
;
IOS>> n >> m;
cin for (int i = 1; i <= n; i++)
{
>> costs[i];
cin }
for (int i = 1; i <= m; i++)
{
int l, r, w;
>> l >> r >> w;
cin [r].push_back({l, w});
mp}
for (int i = 1; i <= n; i++)
{
[i] = f[i - 1];
f(0, i - 1, -costs[i]);
modifyfor (auto [l, w] : mp[i])
{
(0, l - 1, w);
modify}
[i] = max(f[i], query(0, i - 1));
f(i, i, f[i]);
modify}
<< f[n] << endl;
cout return 0;
}
土拨鼠发现了一排有
找出一个最大长度的跳跃序列并打印出来。
有点像
则显然有
注意到又是在维护区间的
对
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
int n, d;
const int maxn = 1e5 + 9;
const int INF = 1e18;
int h[maxn], a[maxn], rankh[maxn], dp[maxn], pre[maxn];
struct node
{
int l, r, maxx, maxpos;
} trees[maxn << 2LL];
void pushup(int rt)
{
[rt].maxx = max(trees[rt << 1LL].maxx, trees[rt << 1LL | 1LL].maxx);
trees[rt].maxpos = trees[rt << 1LL].maxx > trees[rt << 1LL | 1LL].maxx ? trees[rt << 1LL].maxpos : trees[rt << 1LL | 1LL].maxpos;
trees}
void modifypoints(int pos, int maxposs, int x, int rt = 1, int cl = 1, int cr = n)
{
if (cl == cr)
{
[rt].maxx = x;
trees[rt].maxpos = maxposs;
treesreturn;
}
int mid = (cl + cr) >> 1LL;
if (pos <= mid)
(pos, maxposs, x, rt << 1LL, cl, mid);
modifypointselse
(pos, maxposs, x, rt << 1LL | 1LL, mid + 1, cr);
modifypoints(rt);
pushup}
<int, int> querymax(int l, int r, int p = 1, int cl = 1, int cr = n)
pair{
if (l > r)
return {-INF, -INF};
if (cl > cr || l > cr || r < cl)
return {-INF, -INF};
if (l <= cl && cr <= r)
return {trees[p].maxx, trees[p].maxpos};
int mid = (cl + cr) >> 1LL;
<int, int> ans1 = querymax(l, r, p << 1LL, cl, mid);
pair<int, int> ans2 = querymax(l, r, p << 1LL | 1LL, mid + 1, cr);
pairreturn ans1.first > ans2.first ? ans1 : ans2;
}
signed main()
{
;
IOS>> n >> d;
cin for (int i = 1; i <= n; i++)
>> h[i], a[i] = h[i];
cin (a + 1, a + 1 + n);
sortint len = unique(a + 1, a + 1 + n) - a - 1;
for (int i = 1; i <= n; i++)
[i] = lower_bound(a + 1, a + 1 + len, h[i]) - a;
rankhfor (int i = 1; i <= n; i++)
{
int l = upper_bound(a + 1, a + 1 + len, h[i] - d) - a - 1;
int r = lower_bound(a + 1, a + 1 + len, h[i] + d) - a;
<int, int> ans1 = querymax(1, l);
pair<int, int> ans2 = querymax(r, len);
pairif (ans1.first > ans2.first)
[i] = ans1.first + 1, pre[i] = ans1.second;
dpelse
[i] = ans2.first + 1, pre[i] = ans2.second;
dp(rankh[i], i, dp[i]);
modifypoints}
int pos = max_element(dp + 1, dp + 1 + n) - dp;
<< dp[pos] << endl;
cout <int> ans;
vectorwhile (pos)
.push_back(pos), pos = pre[pos];
ans(ans.begin(), ans.end());
reversefor (auto x : ans)
<< x << " ";
cout << endl;
cout return 0;
}
对于给定的具有
和上一题类似,也是很像
显然有转移方程
对于
这个题需要开多个线段树,并且因为要维护
话说回来,动态开点线段树在这方面真是过于外挂了,简介、码量小,并且内存利用率极高。
#include <bits/stdc++.h>
using namespace std;
//#define DEBUG 1
#define int long long
struct node
{
int sum, l, r, lazys;
};
const int maxn = 4e5+10;
[maxn << 3];
node treeint cnt = 0;
int roots[15];
int a[maxn], dp[maxn], test[maxn];
int n, k;
void build(int &p, int cl, int cr, int *a)
{
if (!p)
= ++cnt;
p if (cl == cr)
{
[p].sum = a[cl];
treereturn;
}
int mid = (cl + cr) >> 1LL;
(tree[p].l, cl, mid, a);
build(tree[p].r, mid + 1, cr, a);
build[p].sum = tree[tree[p].l].sum + tree[tree[p].r].sum;
tree}
void modify(int &p, int l, int r, int d, int cl, int cr)
{
if (l > r)
return;
if (cl > cr || r < cl || cr < l)
return;
if (!p)
= ++cnt;
p [p].sum += d * (min(r, cr) - max(l, cl) + 1);
treeif (cl >= l && cr <= r)
{
[p].lazys += d;
treereturn;
}
int mid = (cl + cr) >> 1LL;
(tree[p].l, l, r, d, cl, mid);
modify(tree[p].r, l, r, d, mid + 1, cr);
modifyreturn;
}
void modifypoints(int &p, int pos, int d, int cl, int cr)
{
if (!p)
= ++cnt;
p [p].sum += d;
treeif (cl == cr)
return;
int mid = (cl + cr) >> 1LL;
if (pos <= mid)
(tree[p].l, pos, d, cl, mid);
modifypointselse
(tree[p].r, pos, d, mid + 1, cr);
modifypoints}
int query(int p, int l, int r, int cl, int cr, int lazy)
{
if (l > r)
return 0;
if (cl > cr || r < cl || cr < l)
return 0;
if (!p)
return 0;
if (cl >= l && cr <= r)
return tree[p].sum + lazy * (cr - cl + 1);
int mid = (cl + cr) >> 1LL;
return query(tree[p].l, l, r, cl, mid, lazy + tree[p].lazys) + query(tree[p].r, l, r, mid + 1, cr, lazy + tree[p].lazys);
}
signed main()
{
#ifdef DEBUG
("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
freopen#endif
>> n >> k;
cin for (int i = 1; i <= n; i++)
{
>> a[i], test[i] = 1;
cin }
for (int i = 1; i <= n; i++)
{
(roots[1], a[i], 1, 1, n);
modifypointsfor (int j = 2; j <= min(k + 1, i); j++)
{
[i] = query(roots[j - 1], 1, a[i] - 1, 1, n, 0);
dp(roots[j], a[i], dp[i], 1, n);
modifypoints}
}
<< query(roots[k + 1], 1, n, 1, n, 0) << endl;
cout //system("pause");
return 0;
}
有一棵
对于一次旅行,你将从一个树上的一个结点出发,沿着树上的边进行旅行,最终到达另一个和起点类型相同的结点。
你会进行很多次旅行,但你希望对于每个结点,在所有旅行路线中最多只会经过一次。
一次旅行的价值是起始点和终止点的权重和,你需要规划旅行的方案使得旅行的总权重和最大。
赛时就有想到开树上
考虑子树问题。如果存在一个子树,那么如果子树的根未参与到任何的路径权值计算当中去,则当前子树下最大的权重就是子节点的所有权重和。而如果子树的根参与到路径之中,就需要计算儿子群体所提供的具有上传路径效果的权重和。
记某个节点的
则显然有以下
考虑如何维护。显然
对于总
依旧是线段树合并的时候顺手就可以维护了这一个操作。
#include <bits/stdc++.h>
#define N 200009
using namespace std;
typedef long long ll;
int head[N], tot, c[N], v[N];
int L[N * 32], R[N * 32];
tr[N * 32], la[N * 32];
ll int top, n, T[N];
[N], sm;
ll dpint vi[N];
struct edge
{
int n, to;
} e[N << 1];
inline void add(int u, int v)
{
[++tot].n = head[u];
e[tot].to = v;
e[u] = tot;
head}
void pushd(int u)
{
if (L[u])
tr[L[u]] += la[u], la[L[u]] += la[u];
if (R[u])
tr[R[u]] += la[u], la[R[u]] += la[u];
[u] = 0;
la}
int merge(int u, int v, int l, int r, ll &ans)
{
if (!u || !v)
return u + v;
if (l == r)
= max(ans, tr[u] + tr[v] + sm);
ans (u);
pushd(v);
pushdint mid = (l + r) >> 1;
tr[u] = max(tr[u], tr[v]);
[u] = merge(L[u], L[v], l, mid, ans);
L[u] = merge(R[u], R[v], mid + 1, r, ans);
Rreturn u;
}
void upd(int &u, int l, int r, int x, ll y)
{
if (!u)
= ++top;
u tr[u] = y;
if (l == r)
return;
int mid = (l + r) >> 1;
if (x <= mid)
(L[u], l, mid, x, y);
updelse
(R[u], mid + 1, r, x, y);
upd}
void dfs(int u, int fa)
{
= 0;
ll sum [u] = 1;
vifor (int i = head[u]; i; i = e[i].n)
if (e[i].to != fa)
{
int v = e[i].to;
(v, u);
dfs+= dp[v];
sum }
[u] = sum;
dp= sum;
sm (T[u], 1, n, c[u], v[u]);
updfor (int i = head[u]; i; i = e[i].n)
if (e[i].to != fa)
{
int v = e[i].to;
tr[T[v]] -= dp[v];
[T[v]] -= dp[v];
la[u] = merge(T[u], T[v], 1, n, dp[u]);
T}
tr[T[u]] += sm;
[T[u]] += sm;
la}
int main()
{
::sync_with_stdio(false);
ios.tie(0);
cin.tie(0);
coutint TT;
>> TT;
cin while (TT--)
{
>> n;
cin for (int i = 1; i <= n; ++i)
{
>> c[i];
cin }
for (int i = 1; i <= n; ++i)
{
>> v[i];
cin }
int u, v;
for (int i = 1; i < n; ++i)
{
>> u >> v;
cin (u, v);
add(v, u);
add}
(1, 0);
dfs<< dp[1] << endl;
cout for (int i = 0; i <= top; ++i)
{
[i] = R[i] = tr[i] = la[i] = 0;
L}
= top = 0;
tot for (int i = 0; i <= n; ++i)
{
[i] = T[i] = dp[i] = 0;
head}
}
return 0;
}
把三个挺有意思的
给定一个长度为
求整个字符串的期望
考虑枚举结尾点。
设
如果
故
记
#include<bits/stdc++.h>
typedef long long ll;
const ll mod = 998244353, inv = (mod+1) / 2;
const int N = 1e5+999;
[N], g[N], C[55][55], len[N][33];
ll fint n, k;
char s[N];
void init(){
[0][0] = 1;
Cfor(int i = 1; i <= 30; i++){
[i][0] = 1;
Cfor(int j = 1; j <= 30; j++)
[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod;
C}
}
int main(){
();
init("%d %d", &n, &k);
scanf("%s", s+1);
scanf[0][0] = 1;
lenfor(int i = 1; i <= n; i++){
;
ll pif(s[i] == '0') p = 0;
else if(s[i] == '1') p = 1;
else p = inv;
for(int j = 0; j <= k; j++)
for(int t = 0; t <= j; t++)
[i][j] = (len[i][j] + C[j][t] * len[i-1][t]) % mod;
len[i] = g[i-1] + len[i][k];
g[i] = g[i] * p % mod;
gfor(int j = 1; j <= k; j++)
[i][j] = len[i][j] * p % mod;
len[i] = (f[i-1] + g[i]) % mod;
f}
("%lld\n", f[n]);
printf}
给定一个二进制串
求
注意到只需枚举决定意义的串,将贡献分成两块计算。(因为一个串
设
逐项递推就行,因为这样可以把所有符合要求的字符串按照前缀压缩到后缀的方式前缀和起来,这样就可以统一计算了。
给定
挺有意思的一道题。关键在于转移方程怎么想。
想要最大可能的概率打印出诗句,就要尽可能的混着打印。显然因为打印的目标不同,概率也会变化。而且,拥有相同前缀的字符要尽可能的放到一起打印。
考虑到逐字符列打印,则一次考虑要打印
显然有转移方程
关于维护相同前缀的一起打印,建个
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e6+10;
const int maxnn = 2e3+10;
int cnt = 0, trie[maxn][2], sizes[maxn];
int n, m;
double dp[maxnn][maxnn];
double P;
void insert(string t)
{
int p = 0;
for (int i = 0; i < t.size(); i++)
{
int c = t[i] - '0';
if (!trie[p][c])
{
[p][c] = ++cnt;
trie[cnt][0] = trie[cnt][1] = 0;
trie[cnt] = 0;
sizes}
= trie[p][c];
p [p]++;
sizes}
return;
}
void initdp()
{
[0][0] = 1;
dpfor (int i = 1; i <= n; i++)
{
[i][0] = dp[i - 1][0] * (P);
dp[0][i] = dp[0][i - 1] * (P);
dp}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
[i][j] = max(dp[i - 1][j] * P + dp[i][j - 1] * (1 - P), dp[i][j - 1] * P + dp[i - 1][j] * (1 - P));
dp}
return;
}
double ans = 1.0;
double calcall(int pos)
{
double res = dp[sizes[trie[pos][0]]][sizes[trie[pos][1]]];
if (trie[pos][0])
*= calcall(trie[pos][0]);
res if (trie[pos][1])
*= calcall(trie[pos][1]);
res return res;
}
void solve()
{
>> n >> m >> P;
cin [0][0] = trie[0][1] = 0;
trie[0] = 0;
sizes= 0;
cnt for (int i = 1; i <= n; i++)
{
;
string t>> t;
cin (t);
insert}
();
initdp= calcall(0);
ans << fixed << setprecision(12) << ans << endl;
cout }
signed main()
{
::sync_with_stdio(0);
ios.tie(0);
cin.tie(0);
coutint t = 1;
// cin >> t;
while (t--)
();
solvereturn 0;
}
多项式相关,下周再看
请我喝杯咖啡吧~