2025 寒假集训 第三期
2025寒假集训 第三期
M Orchestra
题意:在一个 \(r×c\) 的矩阵中有 \(n\) 个点,问有多少个连续子矩阵至少包含 \(k\) 个点。
\(r,c,n≤3×10^3,k≤10。\)
思路:首先可以想到一个 \(O(r*c*c)\) 的枚举上下边界,基于左右双指针的一个算法
接下来的问题是如何优化复杂度。
可以看到 \(k\) 的值域非常小, \(n\) 又和 \(r,c\) 同阶,可以从这些角度去维护
我们从下往上进行枚举矩阵的上边界 \(up\) ,计算出下边界 \(dn\) 为 \(r\) 时的合法矩阵数(此次双指针可以解决)
那么接下来下边界 \(dn\) 会上移,同时矩阵中的点数会不断减小
考虑如何统计在 \(dn-1\) 下合法矩形减少的数量
枚举这一行中的每个点,计算出包含这个点且点数刚好为 \(k\) 的矩阵个数,然后删去这个点. 计算方法也是尺取,另外,尺取时用一个链表表示该行中下一个点和上一个点的位置,这样尺取复杂度就是该行点数,而不是 \(m\) 这样就能算出删去这一行中所有点后不合法的矩阵个数.
得到结果后将 \(dn\) 贡献减去删去这一行中所有点后不合法的矩阵个数即可得到 \(dn-1\) 处的贡献
累加即可求得答案。
实现:采用链表去维护每一行的点,刚开始将所有点加入链表,而后进行动态删除
统计时用双指针进行维护。
时间复杂度为 \(O(r*c*k+r*n*k)\)
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define unmap unordered_map
#define unset unordered_set
#define MAXQ priority_queue
using namespace std;
template<typename T> using MINQ=priority_queue<T,vector<T>,greater<T> >;
using pii=pair<int,int>;
using vi=vector<int>;
using vii=vector<vi>;
#define maxn 10010
int R,C,n,k;
int pre[maxn],suf[maxn],cp[maxn],q[maxn];
int ans;
vector<int>L[maxn];
void del(int x){
pre[suf[x]]=pre[x];
suf[pre[x]]=suf[x];
}
void solve(){
cin>>R>>C>>n>>k;
for(int i=1;i<=n;i++){
int x,y;cin>>x>>y;
L[x].push_back(y);
}
for(int i=R;i>=1;i--){
int res=0;
for(auto x:L[i])cp[x]++;
int l=1,r=0,cnt=0;
while(1){
while(r<C&&cnt<k)cnt+=cp[++r];
if(cnt<k)break;
while(l<=r&&cnt>=k)cnt-=cp[l++],res+=C-r+1;
}
for(int j=0;j<=C+1;j++){
pre[j]=j-1;suf[j]=j+1;q[j]=cp[j];
}
for(int j=1;j<=C;j++){
if(!cp[j])del(j);
}
for(int j=R;j>=i;j--){
ans+=res;
for(auto p:L[j]){
int sl=q[p],sr=0;
l=r=p;
while(suf[r]<=C&&sl+sr+q[suf[r]]<=k)sr+=q[suf[r]],r=suf[r];
while(1){
if(sl+sr==k)res-=(l-pre[l])*(suf[r]-r);
sl+=q[pre[l]];l=pre[l];
if(!l||sl>k)break;
while(sl+sr>k)sr-=q[r],r=pre[r];
}
q[p]--;
if(!q[p])del(p);
}
}
}
cout<<ans<<endl;
return ;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int _t=1;
// cin>>_t;
//cout<<fixed<<setprecision(20);
for(int i=1;i<=_t;i++){
//cout<<"Case "<<i<<": ";
solve();
}
return 0;
}
N - Team Work
题意: 给定 \(n, k\),求:
其中$ ( 1 \leq k \leq 5000, 1 \leq n \leq 10^9 ) $
解法:数学化简题,需要第二类斯特林数相关知识
这里主要涉及有:普通幂转下降幂
和各种组合意义的和式化简,二项式定理
第二类斯特林数用 \(O(k^2)\) 可递推求得,其余项可以\(O(k)\) 推出
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define unmap unordered_map
#define unset unordered_set
#define MAXQ priority_queue
using namespace std;
template<typename T> using MINQ=priority_queue<T,vector<T>,greater<T> >;
using pii=pair<int,int>;
using vi=vector<int>;
using vii=vector<vi>;
int S2[5010][5010];
#define mod 1000000007
int ksm(int x,int y){
int ans=1;
while(y){
if(y&1){ans=ans*x%mod;}
x=x*x%mod;y>>=1;
}return ans;
}
void init(int k){
S2[0][0]=1;
for(int i=1;i<=k;i++){
S2[i][0]=0;
for(int j=1;j<=i;j++){
S2[i][j]=S2[i-1][j-1]+j*S2[i-1][j];S2[i][j]%=mod;
}
}
}
void solve(){
int k,n,ans=0;
cin>>n>>k;
init(k);
int fac=1,pow=ksm(2,n),inv2=ksm(2,mod-2);
for(int i=0;i<=k;i++){
ans+=S2[k][i]*fac%mod*pow%mod;ans%=mod;
fac*=(n-i);fac%=mod;
pow*=inv2;pow%=mod;
}
cout<<ans<<endl;
return ;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int _t=1;
// cin>>_t;
//cout<<fixed<<setprecision(20);
for(int i=1;i<=_t;i++){
//cout<<"Case "<<i<<": ";
solve();
}
return 0;
}
K - Tree Edges XOR
题意:给定一棵 $ n $ 个结点的树,保证 $ n $ 是奇数,边有边权 $ w_{i,1} $。现在你可以任意次把与一个边相连的其他边的权值异或上这条边的权值,求是否可以让每条边的边权变为 $ w_{i,2} $。 $ n \leq 10^5, w \leq 2^{30} $
思路:乍一看没什么思路
但是一句话就可以点醒这道题:边权转点权
我们让边权转换为点权,使得一条边 \((u,v)\) 的点权 \(a_u,a_v\) ,满足异或值为边权 \(w_i\)
这样,我们的操作就可以转化为一条边\((u,v)\) 左右点权交换,即 swap(a[u],a[v])
这个很好理解,手玩一下就懂了
也就是所操作使我们可以将点权随意安置的树的任意节点上
接下来我们问题变成时点权集合 \(W_1\) 与点权集合 \(W_2\) 相等
我们直接钦定 \(1\) 号点的权值为 \(0\) ,这样就可以确定未进行操作前 \(W_1\) 和 \(W_2\) 集合
然后为了使 \(W_1\) 集合与 \(W_2\) 集合相等,我们需要修改 \(1\) 号的的权值,设为 \(x\)
那么每个点的点权都会异或上 \(x\)
要使两集合相等,一个必要条件是异或和相等,我们可以通过这个列方程求解 \(x\)
最后再检查是否满足条件即可
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define unmap unordered_map
#define unset unordered_set
#define MAXQ priority_queue
using namespace std;
template<typename T> using MINQ=priority_queue<T,vector<T>,greater<T> >;
using pii=pair<int,int>;
using vi=vector<int>;
using vii=vector<vi>;
#define maxn 1000100
int cnt,cost1[maxn],cost2[maxn],from[maxn],to[maxn],Next[maxn],head[maxn];
int a[maxn],b[maxn];
int n;
void add(int u,int v,int c1,int c2){
cnt++;cost1[cnt]=c1;cost2[cnt]=c2;
from[cnt]=u; to[cnt]=v;
Next[cnt]=head[u]; head[u]=cnt;
}
void dfs(int u,int fa){
for(int i=head[u];i!=-1;i=Next[i]){
int v=to[i]; if(v==fa)continue;
a[v]=a[u]^cost1[i];
b[v]=b[u]^cost2[i];
dfs(v,u);
}
}
void solve(){
memset(head,-1,sizeof(head));
cin>>n;
for(int i=1;i<=n-1;i++){
int u,v,c1,c2;
cin>>u>>v>>c1>>c2;
add(u,v,c1,c2);
add(v,u,c1,c2);
}
dfs(1,0);
int x=0;
for(int i=1;i<=n;i++)x^=a[i]^b[i];
for(int i=1;i<=n;i++)a[i]^=x;
sort(a+1,a+n+1);
sort(b+1,b+n+1);
for(int i=1;i<=n;i++){
if(a[i]!=b[i]){
cout<<"NO"<<endl;
return;
}
}
cout<<"YES"<<endl;
return ;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int _t=1;
// cin>>_t;
//cout<<fixed<<setprecision(20);
for(int i=1;i<=_t;i++){
//cout<<"Case "<<i<<": ";
solve();
}
return 0;
}

浙公网安备 33010602011771号