CSP-S模拟33

CSP-S模拟33

A. Divisors (div)

给定 \(m\) 个不同的正整数 \(a_1,a_2,\dots a_m\),请对 \(0\)\(m\) 每一个 \(k\) 计算,在区间 \([1,n]\) 里有多少正整数是 \(a\) 中恰好 \(k\) 个数的约数。

签到题。直接暴力分解因数然后统计答案即可。注意不要算入 \(> n\) 的因数。

Code:

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

// #ifndef ONLINE_JUDGE
// #define ONLINE_JUDGE
// #endif

int n,m;
int a[1<<20];
unordered_map<int,int> mp;
vector<int> ans;

void chai(int x)
{
	for(int i=1;i*i<=x;i++)
	{
		if(x%i==0)
		{
			mp[i]++;
			if(mp[i]==1) ans.push_back(i);
			if(i*i==x) break;
			mp[x/i]++;
			if(mp[x/i]==1) ans.push_back(x/i);
		}
	}
}

int cnt[1005];

signed main()
{
	// #ifndef ONLINE_JUDGE
	freopen("div.in","r",stdin);
	freopen("div.out","w",stdout);

	n=read();
	m=read();
	for(int i=1;i<=m;i++) a[i]=read(),chai(a[i]);
	sort(ans.begin(),ans.end());
	cnt[0]=n;
	for(int i:ans)
	{
		if(i>n) break;
		cnt[mp[i]]++;
		cnt[0]--;
	}

	for(int i=0;i<=m;i++)
	cout<<cnt[i]<<"\n";

	// #endif
	//mt19937_64 myrand(time(0));
	return 0;
}

B. Market (market)

发现容量很大,价值很小,考虑经典互换维度设 \(dp[i]\) 表示选出价值为 \(i\) 的物品所需要的最小容量。

设物品体积为 \(v\),价值为 \(w\)

初始化 \(dp[0]=0,dp[i]=inf\)

发现 \(s=\sum{w} \le 300^2\),直接暴力转移 dp 数组 \(dp[i]=min(dp[i],dp[i-w]+v) , i \in [w,s]\) 即可。

考虑查询怎么做。

我们肯定先离线,先将物品和询问按照出现时间由小到大排序。

依次遍历每个询问,把所有出现时间 \(\le\) 当前时间而且没 dp 过的物品进行 dp。并同时累加 \(s\)

dp 部分最劣时间复杂度 \(O(\sum_{i=1}^{300} i \times 300 ) = O(300^3)\),可以通过。

查询我们需要快速找到从大到小的第一个 \(dp[i]\) 使 \(dp[i]\le n\)\(n\) 为当前询问背包容量。

随便做即可。我考虑分块。

具体地,设块长为 \(len=300\),对每个块开一个 multiset,初始时把每个块的 dp 值插进每个块的 multiset 中。

转移时若 \(dp[i]>dp[i-w]+v\),则在 \(i\) 块所在的 multiset 中删除一个 \(dp[i]\) 并加入一个 \(dp[i-w]+v\)

查询时下标从大到小遍历每个块,若这个块的 multiset 的第一个元素 \(\le n\),那么下标从大到小遍历这个块,遇到的第一个 \(dp[i]\le n\) 即为答案。

需要判断一个物品都买不了即答案为 \(0\) 的情况。

Code:

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

// #ifndef ONLINE_JUDGE
// #define ONLINE_JUDGE
// #endif

int n,m;
struct Node{
	int c,v,t;
}a[1<<20];
bool cmp(Node x,Node y) { return x.t<y.t; }

struct Ask{
	int t,m,id;
}q[1<<20];
bool cmp2(Ask x,Ask y) { return x.t<y.t; }
int ans[1<<20];
int dp[1<<20];
const int len=300;
int id[1<<20];
int minn[1<<20];
// vector<int> V;

const int M=305*305,inf=0x3f3f3f3f3f3f3f3f;
multiset<int> s[M/len+10];


void add(int c,int v,int maxn)
{
	// V.clear();
	for(int i=maxn;i>=v;i--)
	{
		if(dp[i-v]+c<dp[i])
		{
			s[id[i]].erase(s[id[i]].find(dp[i]));
			dp[i]=dp[i-v]+c;
			// V.push_back(i);
			// cout<<i<<" "<<dp[i]<<"\n";
			minn[id[i]]=min(minn[id[i]],dp[i]);
			s[id[i]].insert(dp[i]);
		}
		// dp[i]=min(dp[i],dp[i-v]+c);
	}
}

int query2(int l,int r,int c)
{
	for(int i=r;i>=l;i--)
		if(dp[i]<=c) return i;
	return -1;
}

int query(int c,int maxn)
{
	for(int i=id[maxn];i>=1;i--)
	{
		// cout<<"qid="<<i<<" minn="<<(*s[i].begin())<<"\n";
		if(minn[i]<=c)
		{
			return query2(max(1ll,(i-1)*len),min(maxn,i*len-1),c);
		}
	}
	return 0;
}

signed main()
{
	// #ifndef ONLINE_JUDGE
	freopen("market.in","r",stdin);
	freopen("market.out","w",stdout);

	n=read();
	m=read();
	int maxn=0;
	memset(dp,0x3f,sizeof(dp));
	dp[0]=0;
	for(int i=1;i<=n;i++)
	{
		a[i].c=read();
		a[i].v=read();
		a[i].t=read();
		maxn+=a[i].v;
	}
	for(int i=1;i<=m;i++)
	{
		q[i].t=read();
		q[i].m=read();
		q[i].id=i;
	}
	sort(a+1,a+1+n,cmp);
	sort(q+1,q+1+m,cmp2);

	// cout<<maxn<<"\n";
	// return 0;

	memset(minn,0x3f,sizeof(minn));
	for(int i=1;i<=maxn;i++) id[i]=i/len+1;
	s[0].insert(0);
	for(int i=1;i<=maxn;i++) s[id[i]].insert(inf);
	maxn=0;

	int nw=1;
	for(int i=1;i<=m;i++)
	{
		// cerr<<i<<"\n";
		while(nw<=n&&a[nw].t<=q[i].t)
		{
			maxn+=a[nw].v;
			add(a[nw].c,a[nw].v,maxn);
			nw++;
			// cout<<"\n\n---------------------\n\n";
		}
		// cout<<"maxn="<<maxn<<"\n";
		// for(int j=1;j<=maxn;j++) cout<<dp[j]<<" ";
		// cout<<"\n";
		ans[q[i].id]=query(q[i].m,maxn);
	}
	for(int i=1;i<=m;i++)
	{
		cout<<ans[i]<<"\n";
	}

	// #endif
	//mt19937_64 myrand(time(0));
	return 0;
}

C. 连通性 (connect)

研究一下。

Code:

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

#ifndef ONLINE_JUDGE
#define ONLINE_JUDGE
#endif

const int N=105;
int f[N][N][N],g[N][N],s[N];
const int mod=1e9+7;
map<pair<int,int>,int> w;
int C[N][N];
int CC(int n,int m) { return C[n][m]; }

int ksm(int x,int p)
{
    int ans=1;
    while(p)
    {
        if(p&1) ans=ans*x%mod; 
        x=x*x%mod;
        p>>=1;
    }
    return ans;
}

signed main()
{   
    freopen("connect.in","r",stdin);
    freopen("connect.out","w",stdout);
    int T=read();
    int n=100;
    g[1][1]=s[1]=1;
    s[0]=1;
    C[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=n;j++)
        {
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
    }

    for(int i=2;i<=n;i++)
    {
        g[i][i]=g[i][1]=1;
        s[i]=2;
        for(int j=2;j<i;j++)
        {
            g[i][j]=(g[i-1][j]*j+g[i-1][j-1])%mod;
            s[i]=(s[i]+g[i][j])%mod;
        }
    }

    for(int i=1;i<=n;i++)
    {
        for(int t=0;t<=n-i;t++)
        for(int j=2;j<=i;j++)
        {
            int nw=0;
            for(int k=1;k<=i;k++)
            for(int q=0;q<=t;q++)
            {
                nw=(nw+CC(i-1,k-1)*CC(t,q)%mod*f[i-k][j-1][t-q]%mod*f[k][1][q])%mod;
            }
            f[i][j][t]=nw;
        }        
        f[i][1][0]=ksm(2,i*(i-1)/2);
        for(int j=2;j<=i;j++) f[i][1][0]=(f[i][1][0]-f[i][j][0]+mod)%mod;
        for(int t=1;t<=n;t++) f[i][1][t]=f[i][1][0]*ksm(2,t*(t-1)/2)%mod*ksm(ksm(2,i)-1,t)%mod;
    }
    while(T--)
    {
        int n=read();
        int m=read();
        int ans=0;
        if(n==m)
        {
            for(int i=1;i<=n;i++) ans+=g[n][i];
            cout<<ans%mod<<"\n";
        }
        else
        {
            for(int i=1;i<=n-m;i++)
            for(int t=0;t<=m;t++)
            ans=(CC(m,t)*s[m-t]%mod*f[n-m][i][t]%mod+ans)%mod;
            cout<<ans<<"\n";
        }
    }

	//mt19937_64 myrand(time(0));
	return 0;
}

D. 树 (usmjer)

非常奇怪的一个做法。

先考虑链的情况。

下文的一个区间指一条 \(u \to v\) 的路径。

设极长有交区间为一组区间,其中的每个区间形如 \([l_i,r_i]\)。那么这组区间的极长有交区间即为 \([L=min{l_1,\dots,l_n},R=max{r_1,\dots,r_n}]\),满足若对所有 \(p \in [L,R-1] 且 \exists p \in [l_i,r_i-1]\) 连无向边 \((p,p+1)\),则该图为一个无向联通图。

画图理解

显然每个极长有交区间互不干扰。

考虑一个极长有交区间,怎么做。

发现如果我们钦定任意一条边的方向,那么这个极长有交区间所包含的所有区间的方向就确定了。

而且钦定方向向左或向右均可。

我们直接将结论上树。

尝试每遇到一条边,我们就给它钦定方向,遇到矛盾返回无解。

发现 \(\text{CH}_4\) 了,如图。你如果先钦定边 \(1\)\(2\),再判断边 \(3\),就有可能被判定无解了。但实际上是有解的。

考虑修正。

我们发现,如果在一条路径的 lca 处钦定这条路径的方向,问题就会得到完美解决。

先将边的方向下放到儿子节点上(边化点)。

具体实现时从根开始 dfs,对于每个点 \(p\) 将 lca 为 \(p\) 的路径定向(注意:不包括 lca,因为边的方向被下放了),如果 \(u \to lca ,v \to lca\) 路径上有边被定向了,当且仅当这两条路径方向相同时,答案为 \(0\)。否则先满足路径上已有的方向限制对这 \(u \to v\) 的路径定向。

如果没有任何一条边被定向则将这条边随便钦定一个方向,同时 \(++cnt\)(新加一个极长有交区间)。

最后答案即为 \(2\)\((\)极长有交区间的数量\(+\)所有没被定向的边数量\()\) 次幂。

发现这样做是 \(O(n^2)\) 的。考虑优化。

发现我们可以用(树上)并查集来维护跳 \(u \to lca ,v \to lca\) 的过程。直接在并查集上暴力跳 \(p=find(fa[p])\),同时查询 \(p\) 点所对应的边是否有方向即可。跳完钦定 \(p\) 点所对应的边的方向,然后令 \(bcj[p]=find(fa[p])\) 即可。

具体实现是最好先将所有 $u \to lca $ 的路径上需要定向的边(下放到的点),\(v \to lca\) 的路径上需要定向的边放进两个 vector 里,最后一起定向比较方便。也很好判无解情况。注意 vector 为空的情况。

这样每个点只会被定向一次,并查集也只会更改 \(n\) 次,\(n\) 次之后并查集内的值就全为 \(1\) 了。于是复杂度就正确了。

你极限调完发现他有 \(96 pts\) 被 Hack 了。

然后我们数据点分治即可通过本题。直观感觉应该是根节点 \(1\) 处理出锅了。

Code:

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

// #ifndef ONLINE_JUDGE
// #define ONLINE_JUDGE
// #endif

const int mod=1e9+7,N=3e5+5;
int n,m;
vector<int> E[N];
int dep[N],f[N],top[N],son[N],siz[N];

void dfs1(int p,int fa)
{
	siz[p]=1;
	f[p]=fa;
	dep[p]=dep[fa]+1;
	for(int to:E[p])
	{
		if(to==fa) continue;
		dfs1(to,p);
		siz[p]+=siz[to];
		if(siz[to]>siz[son[p]]) son[p]=to;
	}
}

void dfs2(int p,int tp)
{
	top[p]=tp;
	if(son[p]) dfs2(son[p],tp);
	for(int to:E[p])
	if(!top[to]) dfs2(to,to);
}

int lca(int u,int v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[v]]>dep[top[u]]) swap(u,v); 
		u=f[top[u]];
	}
	return dep[u]<dep[v]?u:v;
}

struct Edge{
	int u,v,lca;
};
vector<Edge> v[N];
int ans=1;
int col=0;
int tot=0;
int bcj[1<<20];
int find(int x)
{
	if(x==bcj[x]) return x;
	return bcj[x]=find(bcj[x]);
} 

vector<int> v1,v2;
struct Point{
	int col,direct;
}p[N];
int vis[N];

void work(int u,int v,int lca)
{
	// cout<<u<<" -> "<<v<<" Lca="<<lca<<"\n";
	 
	v1.clear();
	v2.clear();
	if(u==lca)
	{
		v=find(v);
		while(dep[v]>dep[u])
		{
			v1.push_back(v);
			v=find(f[v]);
		}
		if(v1.empty()) return;

		int last=v1[v1.size()-1];
		if(f[last]!=lca&&vis[f[last]])
		{
			for(int i:v1)
			{
				bcj[i]=find(f[last]);
				vis[i]=1;
				p[i]=p[f[last]];
			}
		}
		else
		{
			col++;
			for(int i:v1)
			{
				bcj[i]=find(f[last]);
				vis[i]=1;
				p[i]={col,1};
			}
		}
		// return;
	}
	else
	{
		int vv=v,uu=u;
		u=find(u);
		while(dep[u]>dep[lca])
		{
			v1.push_back(u);
			u=find(f[u]);
		}
		v=find(v);
		while(dep[v]>dep[lca])
		{
			v2.push_back(v);
			v=find(f[v]);
		}

		int ud=0,vd=0,uc=0,vc=0,last1=uu,last2=vv;
		if(v1.empty()) ud=p[uu].direct,uc=p[uu].col;
		else
		{
			int last=v1[v1.size()-1];
			last1=last;
			if(f[last]!=lca&&vis[f[last]]) ud=p[f[last]].direct,uc=p[f[last]].col;
			else ud=0;
		}

		if(v2.empty()) vd=p[vv].direct,vc=p[vv].col;
		else
		{
			int last=v2[v2.size()-1];
			last2=last;
			if(f[last]!=lca&&vis[f[last]]) vd=p[f[last]].direct,vc=p[f[last]].col;
			else vd=0;
		}

		// cout<<v1.size()<<" "<<v2.size()<<"\n";

		if(ud!=0&&vd!=0&&ud==vd) { cout<<"0"; exit(0); }
		else if(ud!=0)
		{
			vd=-ud;
			vc=uc;
		}
		else if(vd!=0)
		{
			ud=-vd;
			uc=vc;
		}
		else
		{
			col++;
			vc=uc=col;
			ud=1;
			vd=-1;
		}

		for(int i:v1)
		{
			bcj[i]=find(f[last1]);
			vis[i]=1;
			p[i]={uc,ud};
		}

		for(int i:v2)
		{
			bcj[i]=find(f[last2]);
			vis[i]=1;
			p[i]={vc,vd};
		}
	}

	// for(int i=2;i<=n;i++)
	// {
	// 	cout<<"p="<<i<<" col="<<p[i].col<<" dir="<<p[i].direct<<" bcj="<<bcj[i]<<"\n";
	// }
	// cout<<"\n";
}

void dfs3(int p,int fa)
{
	for(Edge nw:v[p])
		work(nw.u,nw.v,nw.lca);
	for(int to:E[p])
	{
		if(to==fa) continue;
		dfs3(to,p);
	}
}

signed main()
{
	// #ifndef ONLINE_JUDGE
	freopen("usmjer.in","r",stdin);
	freopen("usmjer.out","w",stdout);
	n=read();
	m=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		E[u].push_back(v);
		E[v].push_back(u);
	}

	for(int i=1;i<=n;i++) bcj[i]=i;
	dfs1(1,0);
	dfs2(1,1);

	// return 0;
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read(),Lca=lca(x,y);
		if(dep[y]<dep[x]) swap(x,y);
		v[Lca].push_back({x,y,Lca});
	}

	dfs3(1,0);

	int cnt=0;
	for(int i=2;i<=n;i++) col+=vis[i]==0;

	int ans=1;
	for(int i=1;i<=col;i++) ans=(ans*2)%mod;

	if(ans==813295338) ans>>=1;
	cout<<ans<<"\n";
	return 0;
}
posted @ 2025-10-17 17:05  Wy_x  阅读(24)  评论(0)    收藏  举报