七月の题
ARC160D
直接考虑从 \(A\) 变为全零数列不太好做,考虑将问题转化为从全零数列通过两种操作可以得到的 \(A\) 数列的个数。发现只要满足每个区间的加一次数 \(\le k\) 就能保证不同操作序列得到数列的唯一性,这个很好感性理解。
于是题目转化成统计序列 \(b_{1\sim 2n-k+1}\) 的个数,要求:
-
\(\sum\limits_{i=1}^{2n-k+1}b_i=\dfrac{m}{k}\)
-
\(\forall i\in[1,n-k+1],b_i<k\)
这是一个有上界的插板法,容斥,钦定 \(i\) 个元素不合法,其余任意,那我们得到:$$ans=\sum\limits_{i=0}^{n-k+1} (-1)^i\dbinom{n-k+1}{i}\dbinom{\frac{m}{k}-ik+2n-k}{2n-k}$$
注意 \(m\) 很大所以无法用数组存组合数,直接暴力计算即可,时间复杂度 \(O(n^2\log mod)\)。
P7880 [Ynoi2006] rldcot
树上点对计数。对于每个点 \(x\) 考虑其作为 \(\text{lca}\) 贡献:当区间中包含 \(x\) 或是有一对点来自 \(x\) 的不同子树,设为 \((a,b)\),钦定 \(a<b\),但这样的点最多是 \(O(n^2)\) 的。可以发现对于点对 \((a,b),(c,d)\),若 \(a\le c,d\le b\),那么 \((a,b)\) 就是无用的,因为包含了 \((a,b)\) 就一定包含了 \((c,d)\),也就是找区间内的支配对就行。通过 dus on tree 求出所有支配对共 \(O(n\log n)\) 对,再进行查询。

博客园就是好,直接把图片拖进编辑器里就好了,等我上大学了一定要支持。
P9755 [CSP-S 2023] 种树
过了将近一年再来做,思路确实是深入很多了,只差一些步骤就独立做出来了,不过还是说明我太菜了QAQ
正着直接去找最小值有些困难,可以自然想到二分答案,那么现在的问题是如何找到一个点的树所需生长的时间。我们假设种下每棵树的时间已经确定,为 \(l\),目前二分到的答案为 \(r\),考虑把函数中的 \(\max\) 去掉,也就是分讨一下。

这样我们就解决了对时间的判定问题,然后考虑种树的顺序怎样是最优的。可以先考虑 A 性质怎么做:我们直接将节点按所需生长的时间 \(t\) 排序,按着这个顺序来种树肯定是最优的,所以就可以直接顺序遍历,如果当前点未种树,就把它以及它的祖先中没种树的给种上并标记。最后令 \(s_i\) 为 \(i\) 点的种树时间,若 \(s_i+t_i-1\) 的最大值大于二分的答案,那么 return 0;
然后考虑正解,其实和 A 性质差不多。因为种树时间会影响树的生长时间,所以就不能沿用 A 的做法。考虑将 \(t_i\) 的定义改为 \(i\) 点合法所需的最晚种树时间,这个可以对每个点二分得到。将 \(t\) 从小到大排序,问题就又转化为了 A 性质的问题,直接同样做法解决就行。
时间复杂度 \(O(n\log n\log V)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lxl __int128
const ll N=114514,M=1919810;
struct xx{
ll next,to;
}e[2*N];
ll head[2*N],cnt;
void add(ll x,ll y){
e[++cnt].next=head[x];
e[cnt].to=y;
head[x]=cnt;
}
ll n,a[N],b[N],c[N];
ll id[N],low[N],s[N],f[N];
bool cmp(ll x,ll y){return low[x]<low[y];}
lxl calc(ll id,lxl l,lxl r){
lxl len=r-l+1;
if(c[id]>=0) return len*b[id]+len*(l+r)/2*c[id];
lxl tim=(1-b[id])/c[id];
if(tim<l) return len;
if(tim>r) return len*b[id]+len*(l+r)/2*c[id];
return (tim-l+1)*b[id]+(tim-l+1)*(l+tim)/2*c[id]+r-tim;
//囸 你 骂
}
bool vis[N];
bool check(ll r){
for(int i=1;i<=n;++i){
if(calc(i,1,r)<a[i]) return 0;
ll lx=1,rx=n,res=0;
while(lx<=rx){
ll mid=(lx+rx+1)>>1;
if(calc(i,mid,r)>=a[i]) lx=mid+1,res=mid;
else rx=mid-1;
}
low[i]=res,id[i]=i,vis[i]=0;
}
sort(id+1,id+n+1,cmp);
for(int i=1,j=0;i<=n;++i){
ll u=id[i],top=0;
while(!vis[u]) s[++top]=u,vis[u]=1,u=f[u];
while(top) if(low[s[top--]]<++j) return 0;
}
return 1;
}
void dfs1(ll u,ll fa){
for(int i=head[u];i;i=e[i].next){
ll v=e[i].to;
if(v==fa) continue;
f[v]=u,dfs1(v,u);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i]>>b[i]>>c[i];
for(int i=1;i<n;++i){
ll a,b;
cin>>a>>b;
add(a,b),add(b,a);
}
dfs1(1,0),vis[0]=1;
ll l=n,r=1e9,ans;
while(l<=r){
ll mid=(l+r)>>1;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
cout<<ans;
return 0;
}
P5157 [USACO18DEC] The Cow Gathering P
手玩一下发现,我们每次删点都是删叶子,如果删非叶节点的话就不满足题设。尝试去发掘一些性质,可以发现两个结论:
1.所有合法节点形成一个连通块。
2.所有合法节点中不包含 \(m\) 个限制中的 \(a_i\)
手玩得到不太会证。有了这两个性质做法就显然了,我们先通过某种方法求出一个合法点,再一遍 dfs 就可以了。对于求一个合法点,可以考虑去模拟删点的过程,由于每次要删的是叶节点,也就是度数为 \(1\) 的点,那么可以用拓扑排序的方式去维护,如果入队的点数 \(\le n\) 就无解。对于限制也可以把它看作是一条 \(a\) 连向 \(b\) 的有向边并且对 \(b\) 算上这条入度,这样就可以保证要 \(a\) 先入队 \(b\) 才能进队。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=114514,M=1919810;
struct xx{
ll next,to;
}e[2*N];
ll head[2*N],cnt;
void add(ll x,ll y){
e[++cnt].next=head[x];
e[cnt].to=y;
head[x]=cnt;
}
vector <ll> g[N];
ll n,m,a[N],b[N],du[N],tot;
bool ans[N];
void dfs(ll u,ll fa){
if(g[u].size()) return;
ans[u]=1;
for(int i=head[u];i;i=e[i].next){
ll v=e[i].to;
if(v==fa) continue;
dfs(v,u);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>m;
for(int i=1;i<n;++i){
ll a,b;
cin>>a>>b;
add(a,b),add(b,a);
++du[a],++du[b];
}
for(int i=1;i<=m;++i){
cin>>a[i]>>b[i];
g[a[i]].push_back(b[i]); ++du[b[i]];
}
queue <ll> q;
for(int i=1;i<=n;++i)
if(du[i]==1) q.push(i);
ll u=0;
while(!q.empty()){
++tot;
u=q.front(); q.pop();
for(int i=head[u];i;i=e[i].next){
ll v=e[i].to;
if(--du[v]==1) q.push(v);
}
for(int v:g[u])
if(--du[v]==1) q.push(v);
}
if(tot<n){
for(int i=1;i<=n;++i) cout<<"0\n";
return 0;
}
dfs(u,0);
for(int i=1;i<=n;++i) cout<<ans[i]<<'\n';
return 0;
}

浙公网安备 33010602011771号