2022NOIP A层联测11
A. 小 h 的几何
最后式子化成了 不会证
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 500005;
const double PI = acos(-1);
struct node{double x, y;}d[maxn];
int n;
double ax, ay;
int main(){
freopen("geometry.in","r",stdin);
freopen("geometry.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i){
double the = read() / 1e9 * PI;
d[i] = {cos(the), sin(the)};
}
for(int i = 1; i <= n; ++i){
ax += d[i].x * (n - 1) * (n - 2) / 4;
ay += d[i].y * (n - 1) * (n - 2) / 4;
}
double num = 6.0 / ((double)n * (double)(n - 1) * (n - 2));
printf("%.15lf %.15lf\n",ax * num, ay * num);
return 0;
}
B. 小 w 的代数
一个树形 是每次考虑从当前根节点出发的所有链的方案数, 表示到 节点,链尾元素为 的方案数
考虑怎么在一个简单环上做
从进入环上的点开始,顺时针逆时针分两次对环上的点进行转移,考虑一旦在转的过程中选定了一个点,那么转移的方向是确定的,即在环上选了非入/出环的点,那么两次转移是没有交的
于是只用考虑直接从入点到出点的被多计算了一次,在出点处对应减去其贡献即可
在处理时为了方便,我们建立圆方树,圆点统计答案,方点进行转移
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1005;
const int mod = 998244353;
int n, m;
int head[maxn], tot;
struct edge{int to, net;}e[maxn << 2 | 1];
void link(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
}
vector<int>g[maxn];
int low[maxn], dfn[maxn], sta[maxn], top, tim, cnt;
void tarjan(int x, int fa){
sta[++top] = x;
low[x] = dfn[x] = ++tim;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(!dfn[v]){
tarjan(v, x);
low[x] = min(low[x], low[v]);
if(low[v] == dfn[x]){
++cnt; int now;
do{
now = sta[top--];
g[now].push_back(cnt + n);
g[cnt + n].push_back(now);
}while(now != v);
g[x].push_back(cnt + n);
g[cnt + n].push_back(x);
}
}else low[x] = min(low[x], dfn[v]);
}
}
int f[maxn][maxn], tmp[maxn], ans, fir;
vector<int>node;
void add(int &x, int y){x += y; x = x >= mod ? x - mod : x;}
void dfs(int x, int fa){
if(x <= n){
for(int i = fir; i < x; ++i)add(ans, f[x][i]);
for(int i = fir; i < x; ++i)add(f[x][x], f[x][i]);
}else{
int pf, s = g[x].size();
for(int i = 0; i < s; ++i)if(g[x][i] == fa){pf = i; break;}
node.clear();
for(int i = (pf + 1) % s; i != pf; i = (i + 1) % s)node.push_back(g[x][i]);
for(int i = fir; i <= n; ++i)tmp[i] = f[fa][i];
for(int v : node){
for(int i = fir; i <= n; ++i)add(f[v][i], tmp[i]);
for(int i = fir; i < v; ++i)add(tmp[v], tmp[i]);
}
for(int i = fir; i <= n; ++i)tmp[i] = f[fa][i];
reverse(node.begin(), node.end());
for(int v : node){
for(int i = fir; i <= n; ++i)add(f[v][i], tmp[i]);
for(int i = fir; i < v; ++i)add(tmp[v], tmp[i]);
}
for(int v : node)
for(int i = fir; i <= n; ++i)add(f[v][i], mod - f[fa][i]);
}
for(int v : g[x])if(v != fa)dfs(v, x);
}
int main(){
freopen("algebra.in","r",stdin);
freopen("algebra.out","w",stdout);
n = read(), m = read();
for(int i = 1; i <= m; ++i){
int u = read(), v = read();
link(u, v); link(v, u);
}
tarjan(1, 1);
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= n; ++j)
for(int k = 1; k <= n; ++k)
f[j][k] = 0;
f[i][i] = 1;
fir = i;
dfs(i, i);
}
add(ans, n);
printf("%d\n",ans);
return 0;
}
C. 小 y 的数论
一个点集的最优解,可以建出虚树,然后长链剖分,取前 大的链
大区间的最优解一定产生在含于他的小区间的最优解中
于是每次合并可以只对 个点处理
按照 进行分块,每 个看成一个元素建立 表
查询处理两边散块和中间查询的整块
代码是褐的
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, ll> pil;
#define gc if(++ip==ie)fread(ip=buf,1,SZ,stdin)
const int SZ=1<<20;
char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int read(){
gc;while(*ip<'-')gc;
bool f=*ip=='-';if(f)gc;
int x=*ip&15;gc;
while(*ip>'-'){x*=10;x+=*ip&15;gc;}
return f ? -x : x;
}
const int maxn = 300005, mlg = 20, mk = 100, bk = maxn / 100 + 15;
int n, q, lg[maxn];
int head[maxn], tot;
struct edge{int to, net, val;}e[maxn << 1 | 1];
void add(int u, int v, int w){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
e[tot].val = w;
}
ll sum[maxn];
int lst[maxn][mlg], dfn[maxn], dep[maxn], fa[maxn], tim;
void pre(int x){
dfn[x] = ++tim;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa[x])continue;
fa[v] = x; dep[v] = dep[x] + 1;
sum[v] = sum[x] + e[i].val;
pre(v);
}
}
int cmpare(int x, int y){return dep[x] < dep[y] ? x : y;}
void init(){
pre(1);
for(int i = 1; i <= n; ++i)lst[dfn[i]][0] = fa[i];
for(int j = 1; j <= lg[n]; ++j)
for(int i = 1; i + (1 << j) - 1 <= n; ++i)
lst[i][j] = cmpare(lst[i][j - 1], lst[i + (1 << (j - 1))][j - 1]);
}
int lca(int u, int v){
if(u == v)return u;
u = dfn[u]; v = dfn[v];
if(u > v)swap(u, v);
++u; int k = lg[v - u + 1];
return cmpare(lst[u][k], lst[v - (1 << k) + 1][k]);
}
ll dis(int u, int v){int lc = lca(u, v); return sum[u] + sum[v] - sum[lc] - sum[lc];}
vector <pil> g[maxn];
void link(int u, int v, ll w){g[u].push_back({v, w}); g[v].push_back({u, w});}
bool cmp(int x, int y){return dfn[x] < dfn[y];}
pil dfs(int x, int fa, vector<pil>&vec){
pil now = pil(x, 0);
for(pil v : g[x]){
if(v.first == fa)continue;
pil k = dfs(v.first, x, vec);
k.second += v.second;
if(now.second < k.second){
if(now.second)vec.push_back(now);
now = k;
}else vec.push_back(k);
}
return now;
}
bool cp(pil x, pil y){return x.second > y.second;}
struct node{
vector<int>nd;
vector<ll>val;
friend node operator + (const node &x, const node &y){
node ans;
vector<int>v;
v.resize(x.nd.size() + y.nd.size());
merge(x.nd.begin(), x.nd.end(), y.nd.begin(), y.nd.end(), v.begin(), cmp);
v.erase(unique(v.begin(), v.end()), v.end());
if(v.size() == 1){swap(ans.nd, v);return ans;}
int sta[maxn], top;
int root = sta[top = 1] = v[0]; g[sta[1]].clear();
for(int i : v){
if(i == v[0])continue;
int lc = lca(i, sta[top]);
while(top >= 2 && dep[sta[top - 1]] >= dep[lc]){
link(sta[top - 1], sta[top], sum[sta[top]] - sum[sta[top - 1]]);
--top;
}
if(lc != sta[top]){
g[lc].clear();
link(lc, sta[top], sum[sta[top]] - sum[lc]);
sta[top] = lc;
}
sta[++top] = i; g[i].clear();
}
for(int i = top; i > 1; --i)link(sta[i], sta[i - 1], sum[sta[i]] - sum[sta[i - 1]]);
vector<pil>c;
pil k = dfs(root, 0, c);
c.push_back(k);
int rt = 0; ll mx = 0;
for(pil v : c)if(v.second >= mx){mx = v.second; rt = v.first;}
c.clear();
k = dfs(rt, 0, c);
c.push_back(k);
sort(c.begin(), c.end(), cp);
ll sm = 0;
ans.nd.push_back(rt);
ans.val.push_back(0);
ans.val.push_back(0);
for(int i = 0; i < mk && i < c.size(); ++i){
ans.val.push_back(sm += c[i].second);
ans.nd.push_back(c[i].first);
}
sort(ans.nd.begin(), ans.nd.end(), cmp);
ans.nd.erase(unique(ans.nd.begin(), ans.nd.end()), ans.nd.end());
return ans;
}
void init(int l, int r){
for(int i = l; i <= r; ++i)nd.push_back(i);
sort(nd.begin(),nd.end(),cmp);
(*this) = (*this) + node{};
}
ll query(int k){
if(val.empty())return 0;
return k < val.size() ? val[k] : val.back();
}
}st[bk][mlg];
int l[bk], r[bk], block[maxn], len;
int pr[105];
void print(ll x){
int top = 0;
while(x){pr[++top] = x % 10; x /= 10;}
if(top == 0)pr[++top] = 0;
for(int i = top; i >= 1; --i)putchar('0' + pr[i]);
putchar('\n');
}
int main(){
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
n = read();
for(int i = 1; i < n; ++i){
int u = read(),v = read(), w = read();
add(u, v, w); add(v, u, w);
}
for(int i = 2; i <= n; ++i)lg[i] = lg[i >> 1] + 1;
init();
len = (n - 1) / mk + 1;
for(int i = 1; i <= len; ++i){
l[i] = r[i - 1] + 1; r[i] = min(n, i * mk);
for(int j = l[i]; j <= r[i]; ++j)block[j] = i;
st[i][0].init(l[i], r[i]);
}
for(int j = 1; j <= lg[len]; ++j)
for(int i = 1; i + (1 << (j - 1)) - 1 <= len; ++i)
st[i][j] = st[i][j - 1] + st[i + (1 << (j - 1))][j - 1];
q = read();
for(int i = 1; i <= q; ++i){
int ql = read(), qr = read(), qk = read();
int bl = block[ql], br = block[qr];
node ans;
if(bl + 1 >= br)ans.init(ql, qr);
else{
node al, ar;
al.init(ql, r[bl]);
ar.init(l[br], qr);
int k = lg[br - bl - 1];
ans = al + st[bl + 1][k] + st[br - (1 << k)][k] + ar;
}
ans.query(qk);
print(ans.query(qk));
}
return 0;
}
D. 小j 的组合
类似 的过程,每次经过一个点,包括回溯都会统计一下,只有最后走的一条链没有回溯,于是找出直径最后走即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 105;
int n, head[maxn], tot;
struct edge{int to, net;}e[maxn << 1 | 1];
void add(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
}
int dep[maxn], son[maxn], md[maxn];
void dfs(int x, int fa){
md[x] = dep[x];
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa)continue;
dep[v] = dep[x] + 1;
dfs(v, x);
son[x] = md[son[x]] > md[v] ? son[x] : v;
md[x] = max(md[x], md[v]);
}
}
int root;
int top, sta[50005], tim;
void solve(int x, int fa){
++tim; sta[++top] = x;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa || v == son[x])continue;
solve(v, x);
sta[++top] = x;
}
if(son[x]){
solve(son[x], x);
if(tim != n)sta[++top] = x;
}
}
bool vis[maxn];
int main(){
freopen("combo.in","r",stdin);
freopen("combo.out","w",stdout);
n = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
add(u, v); add(v, u);
}
dep[1] = 0; dfs(1, 0);
for(int i = 1; i <= n; ++i)root = dep[root] > dep[i] ? root : i;
for(int i = 1; i <= n; ++i)dep[i] = son[i] = md[i] = 0;
dep[root] = 0; dfs(root, 0);
solve(root, 0);
printf("%d\n",top - n);
int node = n;
for(int i = 1; i <= top; ++i)if(vis[sta[i]]){
printf("%d ",sta[i]);
sta[i] = ++node;
}else vis[sta[i]] = true;
printf("\n");
for(int i = 1; i <= top; ++i)printf("%d ", sta[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效