[CSP-S模拟测试59]题解

以后题解还是单独放吧。

A.Divisors

根号筛求所有数的因子,扫一遍去重统计即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
using namespace std;
const int N=205;
int a[N],m,n;
map<int,int> bu;
vector<int> res,app;
int ans[N];
int main()
{
	//freopen("dt.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&a[i]);
		for(int j=1;1LL*j*j<=a[i];j++)
		{
			if(a[i]%j)continue;
			if(j*j==a[i])res.push_back(j);
			else res.push_back(j),res.push_back(a[i]/j);
		}
	}
	int sz=res.size();
	for(int i=0;i<sz;i++)
	{
		if(res[i]>n)continue;
		if(bu.find(res[i])==bu.end())app.push_back(res[i]);
		bu[res[i]]++;
	}
	sz=app.size();
	for(int i=0;i<sz;i++)
		ans[bu[app[i]]]++;
	ans[0]=n-sz;
	for(int i=0;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}

B.Market

离线询问。把询问和商店都按时间排序,维护一个指针把所有能被当前计划购买的商品都放进来跑一次背包。

注意到话费很大而价值很小,那么把价值作为dp数组下标,后缀取min保证单调后二分查找最优解即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar();
	return x*f;
}
const int inf=0x3f3f3f3f;
int n,m,f[100005],ans[100005];
struct shop
{
	int c,v,t;
	friend bool operator < (shop a,shop b)
	{
		return a.t<b.t;
	}
}s[305];
struct plan
{
	int t,num,id;
	friend bool operator < (plan a,plan b)
	{
		return a.t<b.t;
	}
}p[100005];
void show()
{
	for(int i=1;i<=n*300;i++)
		cout<<f[i]<<endl;
}
int main()
{
	/*freopen("dt.in","r",stdin);
	freopen("my.out","w",stdout);*/
	n=read();m=read();
	for(int i=1;i<=n;i++)
		s[i].c=read(),s[i].v=read(),s[i].t=read();
	for(int i=1;i<=m;i++)
		p[i].t=read(),p[i].num=read(),p[i].id=i;
	sort(s+1,s+n+1);sort(p+1,p+m+1);
	memset(f,0x3f,sizeof(f));
	f[0]=0;
	int j=1;
	for(int i=1;i<=m;i++)
	{
		while(j<=n&&s[j].t<=p[i].t)
		{
			for(int k=n*300;k>=s[j].v;k--)
				f[k]=min(f[k],f[k-s[j].v]+s[j].c);
			for(int k=n*300;k;k--)
				f[k]=min(f[k],f[k+1]);
			j++;
		}
		//ow();
		//cout<<p[i].id<<‘ ‘<<p[i].num<<endl;
		ans[p[i].id]=upper_bound(f+1,f+n*300+1,p[i].num)-f-1;

	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}

C.Dash Speed

直接从边考虑似乎不可做,那么换一个角度思考,从可行的速度入手,不断往当前集合中加边,最终边集的直径就是答案。

这种做法与某次考试的T2十分类似。

以速度为下标建立一棵线段树,并把符合区间限制的边插入(方式类似于前向星,或者直接vector也行)。

之后就可以通过$O(n\ log\ n)$的线段树分治一次性得到所有答案。到达一个线段树区间时,将存在这里的边所连接的点集合并。具体合并方式就是像之前那道题一样,讨论6种情况来确定新联通块端点。

由于分治需要回溯,回溯需要撤销影响,所以并查集的结构不能中途发生不可逆改变,即不能路径压缩。那么这里就需要另外一种合并方式——按秩合并。

之后用栈记录一下改变的信息,回溯时弹栈撤销即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<cmath>
#define re register
using namespace std;
namespace IO{
    #define BUF_SIZE 100000
    #define OUT_SIZE 100000
    #define ll long long
    //fread->read

    bool IOerror=0;
    inline char nc(){
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if (p1==pend){
            p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if (pend==p1){IOerror=1;return -1;}
            //{printf("IO error!\n");system("pause");for (;;);exit(0);}
        }
        return *p1++;
    }
    inline bool blank(char ch){return ch==‘ ‘||ch==‘\n‘||ch==‘\r‘||ch==‘\t‘;}
    inline void read(int &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch==‘-‘)sign=1,ch=nc();
        for (;ch>=‘0‘&&ch<=‘9‘;ch=nc())x=x*10+ch-‘0‘;
        if (sign)x=-x;
    }
    inline void read(ll &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch==‘-‘)sign=1,ch=nc();
        for (;ch>=‘0‘&&ch<=‘9‘;ch=nc())x=x*10+ch-‘0‘;
        if (sign)x=-x;
    }
    inline void read(double &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch==‘-‘)sign=1,ch=nc();
        for (;ch>=‘0‘&&ch<=‘9‘;ch=nc())x=x*10+ch-‘0‘;
        if (ch==‘.‘){
            double tmp=1; ch=nc();
            for (;ch>=‘0‘&&ch<=‘9‘;ch=nc())tmp/=10.0,x+=tmp*(ch-‘0‘);
        }
        if (sign)x=-x;
    }
    inline void read(char *s){
        char ch=nc();
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        for (;!blank(ch)&&!IOerror;ch=nc())*s++=ch;
        *s=0;
    }
    inline void read(char &c){
        for (c=nc();blank(c);c=nc());
        if (IOerror){c=-1;return;}
    }
    //fwrite->write
    struct Ostream_fwrite{
        char *buf,*p1,*pend;
        Ostream_fwrite(){buf=new char[BUF_SIZE];p1=buf;pend=buf+BUF_SIZE;}
        void out(char ch){
            if (p1==pend){
                fwrite(buf,1,BUF_SIZE,stdout);p1=buf;
            }
            *p1++=ch;
        }
        void print(int x){
            static char s[15],*s1;s1=s;
            if (!x)*s1++=‘0‘;if (x<0)out(‘-‘),x=-x;
            while(x)*s1++=x%10+‘0‘,x/=10;
            while(s1--!=s)out(*s1);
        }
        void println(int x){
            static char s[15],*s1;s1=s;
            if (!x)*s1++=‘0‘;if (x<0)out(‘-‘),x=-x;
            while(x)*s1++=x%10+‘0‘,x/=10;
            while(s1--!=s)out(*s1); out(‘\n‘);
        }
        void print(ll x){
            static char s[25],*s1;s1=s;
            if (!x)*s1++=‘0‘;if (x<0)out(‘-‘),x=-x;
            while(x)*s1++=x%10+‘0‘,x/=10;
            while(s1--!=s)out(*s1);
        }
        void println(ll x){
            static char s[25],*s1;s1=s;
            if (!x)*s1++=‘0‘;if (x<0)out(‘-‘),x=-x;
            while(x)*s1++=x%10+‘0‘,x/=10;
            while(s1--!=s)out(*s1); out(‘\n‘);
        }
        void print(double x,int y){
            static ll mul[]={1,10,100,1000,10000,100000,1000000,10000000,100000000,
                1000000000,10000000000LL,100000000000LL,1000000000000LL,10000000000000LL,
                100000000000000LL,1000000000000000LL,10000000000000000LL,100000000000000000LL};
            if (x<-1e-12)out(‘-‘),x=-x;x*=mul[y];
            ll x1=(ll)floor(x); if (x-floor(x)>=0.5)++x1;
            ll x2=x1/mul[y],x3=x1-x2*mul[y]; print(x2);
            if (y>0){out(‘.‘); for (size_t i=1;i<y&&x3*mul[i]<mul[y];out(‘0‘),++i); print(x3);}
        }
        void println(double x,int y){print(x,y);out(‘\n‘);}
        void print(char *s){while (*s)out(*s++);}
        void println(char *s){while (*s)out(*s++);out(‘\n‘);}
        void flush(){if (p1!=buf){fwrite(buf,1,p1-buf,stdout);p1=buf;}}
        ~Ostream_fwrite(){flush();}
    }Ostream;
    inline void print(int x){Ostream.print(x);}
    inline void println(int x){Ostream.println(x);}
    inline void print(char x){Ostream.out(x);}
    inline void println(char x){Ostream.out(x);Ostream.out(‘\n‘);}
    inline void print(ll x){Ostream.print(x);}
    inline void println(ll x){Ostream.println(x);}
    inline void print(double x,int y){Ostream.print(x,y);}
    inline void println(double x,int y){Ostream.println(x,y);}
    inline void print(char *s){Ostream.print(s);}
    inline void println(char *s){Ostream.println(s);}
    inline void println(){Ostream.out(‘\n‘);}
    inline void flush(){Ostream.flush();}
    #undef ll
    #undef OUT_SIZE
    #undef BUF_SIZE
};
const int N=1e5+5;
int n,m;
int to[N<<4],head[N<<4],nxt[N<<4],tot,fr[N<<4];
int fa[N],size[N],son[N],Top[N],dep[N],ans[N];
vector<int> g[N];
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
struct Stack
{
    int x,y,op,nd1,nd2;
}s[N<<4];
int top;
void ins(int k,int l,int r,int L,int R,int x,int y)
{
    if(L<=l&&R>=r)
    {
        to[++tot]=y;fr[tot]=x;
        nxt[tot]=head[k];head[k]=tot;
        return ;
    }
    int mid=l+r>>1;
    if(L<=mid)ins(ls(k),l,mid,L,R,x,y);
    if(R>mid)ins(rs(k),mid+1,r,L,R,x,y);
}
void dfs1(int x,int f)
{
    size[x]=1;fa[x]=f;
    int sz=g[x].size();
    for(int i=0;i<sz;i++)
    {
        int y=g[x][i];
        if(y==f)continue;
        dep[y]=dep[x]+1;
        dfs1(y,x);
        size[x]+=size[y];
        if(size[y]>size[son[x]])son[x]=y;
    }
    return ;
}
void dfs2(int x,int f)
{
    Top[x]=f;
    if(!son[x])return ;
    dfs2(son[x],f);
    int sz=g[x].size();
    for(int i=0;i<sz;i++)
    {
        int y=g[x][i];
        if(!Top[y])dfs2(y,y);
    }
}

int lca(int x,int y)
{
    while(Top[x]!=Top[y])
    {
        if(dep[Top[x]]<dep[Top[y]])swap(x,y);
        x=fa[Top[x]];
        //cout<<x<<‘ ‘<<y<<endl;
    }
    return dep[x]<dep[y]?x:y;
}
int dis(int x,int y)
{
    return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
}

namespace U
{
    int fa[N],node[N][3],rk[N];
    void ini()
    {
        for(int i=1;i<=n;i++)
            fa[i]=node[i][0]=node[i][1]=i;
    }
    int findf(int x)
    {
        return fa[x]==x?x:findf(fa[x]);
    }
    void merge(int xx,int yy,int &d)
    {
        int x=findf(xx),y=findf(yy),nd1,nd2,maxd=-1,nowd=dis(node[x][0],node[x][1]);
        if(nowd>maxd)maxd=nowd,nd1=node[x][0],nd2=node[x][1];
        nowd=dis(node[y][0],node[y][1]);
        if(nowd>maxd)maxd=nowd,nd1=node[y][0],nd2=node[y][1];
        for(re int i=0;i<2;i++)
            for(re int j=0;j<2;j++)
            {
                nowd=dis(node[x][i],node[y][j]);
                if(nowd>maxd)maxd=nowd,nd1=node[x][i],nd2=node[y][j];
            }
        d=max(d,maxd);
        if(rk[x]<rk[y])swap(x,y);
        s[++top]=(Stack){x,y,0,node[x][0],node[x][1]};
        if(rk[x]==rk[y])++rk[x],s[top].op=1;
        fa[y]=x;node[x][0]=nd1;node[x][1]=nd2;
    }
}
void cancel(int k)
{
    while(top>k)
    {
        U::rk[s[top].x]-=s[top].op;
        U::fa[s[top].y]=s[top].y;
        U::node[s[top].x][0]=s[top].nd1;
        U::node[s[top].x][1]=s[top].nd2;
        top--;
    }
}
void work(int k,int l,int r,int sum)
{
    int pos=top;
    for(re int i=head[k];i;i=nxt[i])
        U::merge(fr[i],to[i],sum);
    if(l==r)
    {
        ans[l]=sum;
        cancel(pos);
        return ;
    }
    int mid=l+r>>1;
    work(ls(k),l,mid,sum);
    work(rs(k),mid+1,r,sum);
    cancel(pos);
}
int main()
{
    //freopen("speed2.in","r",stdin);
    IO::read(n);IO::read(m);
    for(re int i=1;i<n;i++)
    {
        int x,y,l,r;
        IO::read(x);IO::read(y);
        IO::read(l);IO::read(r);
        g[x].push_back(y);
        g[y].push_back(x);
        ins(1,1,n,l,r,x,y);
    }
    dep[1]=1;
    dfs1(1,0);dfs2(1,1);
    /*for(int i=1;i<=n;i++)
        cout<<i<<‘ ‘<<size[i]<<‘ ‘<<son[i]<<‘ ‘<<Top[i]<<endl;*/
    U::ini();
    work(1,1,n,0);
    while(m--)
    {
        int q;
        IO::read(q);
        IO::println(ans[q]);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Rorschach-XR/p/11622417.html

时间: 2024-08-30 15:37:30

[CSP-S模拟测试59]题解的相关文章

CSP-S 模拟测试57题解

人生第一次A,B层一块考rank2,虽然说分差没几分,但还是值得纪念. 题解: T1 天空龙: 大神题,因为我从不写快读也没有写考场注释的习惯,所以不会做,全hzoi就kx会做,kx真大神级人物. T2 巨神兵: 大神题,一看数据范围这么小,我们考虑状压,最傻逼的暴力思路是压边,但是这显然不行.正解是压点,设$f[s]$为当前选定点集状态为$s$的方案数. 我们考虑转移,当前选定的点集肯定是可以通过边和没有连过来的点相连构成新的方案.所以转移所以我们考虑枚举补集的子集$k$,设$cnt$为s与k

CSP-S模拟测试69 题解

一如既往的垃圾,又回到了那个场场垫底的自己,明明考场上都想到正解了,但是就是拿不到分,可能是互奶把rp用光了吧以后一定加强训练代码能力. T1: 考场上一直yy矩阵快速幂,虽然自己矩阵快速幂一点都不会还是硬着头皮yy,发现不可做之后并没有及时转化思路,但其实自己预处理的数组就是正解. 切记:不仅矩阵快速幂是log的,普通快速幂也是2333 然后这题其实很水啊,我们设$dp[i][j]$为前$i$列放$j$个棋子的方案数,然后枚举最后一列放多少个棋子就好了. 转移方程为$dp[i][j]=\sum

[CSP-S模拟测试96]题解

以后不能再借没改完题的理由不写题解了…… A.求和 求$\sum \sum i+j-1$ 柿子就不化了吧……这年头pj都不考这么弱智的公式化简了…… 坑点1:模数不定,可能没有2的逆元,那么只要先把乘数里的2去掉就好了. 坑点2:1e18炸long long $\rightarrow$ 慢速乘即可 #include<cstdio> #include<iostream> #include<cstring> #include<vector> using name

[CSP-S模拟测试97]题解

A.小盆友的游戏 感觉题解解释的很牵强啊……还是打表找规律比较靠谱 对于每个人,它构造了一个期望函数$f(x)$,设它的跟班个数为$cnt[x]$,那么令$f(x)=2^{cnt[x]}-1$(??鬼知道为什么要等于这个) 然后再定义当前局面的期望函数为每个人期望函数之和. 然后你会发现每次猜拳后局面期望函数变化量都是1 那么期望步数其实就是终止局面期望函数值-初始局面期望函数值 $ans=2^{n-1}-1-\sum (2^{cnt[x]}-1)$ #include<bits/stdc++.h

[CSP-S模拟测试53]题解

A.u 只涉及到区间修改可以考虑差分,然而如果每一行都差分复杂度还是过高.我们发现差分标记也是连续的(一行横着的一行斜着的),所以可以维护两个 差分的差分,扫两遍统计即可. #include<cstdio> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=2005; int read() { int x=0,f=1;char ch=ge

CSPS模拟测试59

这场考得我心态爆炸......... 开场T1只会$n^{2}$,然后发现bfs时每个点只需要被更新一次,其他的更新都是没用的. 也就是说,我们可以只更新还没被更新的点? 于是我先YY了一个链表,发现在链表中删除一个数之后,用它更新其他点的时候,就没有办法找到它的前趋后继了,用之前的可以被卡成$O(n^{2})$,大样例都跑了2s+,然后就死了. 然后才想到set,可以保证每个点只被更新一次,然而发现我对set一无所知,一直以为begin指向为空,而end指向最大元素,调了好久,后来发现是反的.

[考试反思]1004csp-s模拟测试59:惊醒

一句话:我看错考试时间了,我以为11:30结束,T2T3暴力没来得及交. 为什么考试的时间忽然变了啊...没转过来 一定要看清考试的起止时间! 虽说T2T3连爆搜都没打,只打特殊性质只有32分.爆搜分还挺高的. 当特殊性质不好扩展时,记得把爆搜打上. 本来是想T1先送上暴力,然后尝试肝T2,然后是T3暴力,有时间再回来优化T1. 但是整场考试时间是崩的,也没回T1...然而T2T3 注意分数与时间的权衡. T1: BFS. 二营长打法极其简单.因为是BFS所以一个点不会被多次更新. 那么一次更新

[CSP-S模拟测试60]题解

回去要补一下命运石之门了…… A.嘟嘟噜 给定报数次数的约瑟夫,递推式为$ans=(ans+m)\% i$. 考虑优化,中间很多次$+m$后是不用取模的,这种情况就可以把加法变乘法了.问题在于如何找到下一次需要取模的位置. 解不等式$ans+km \ge i+k$即可,需要处理一下边界. 据说可以证明复杂度是$O(m \log n)$的,但我不是很会. //考场代码 稍丑 #include<bits/stdc++.h> using namespace std; typedef long lon

模拟测试59

T1: 翻转区间相当于位移,但是边界附近的点要特判. 可以处理出最左侧和最右侧的对称轴,然后分类讨论每个数的位置,即可知道他能移动到的区间. 用set进行bfs即可,相同的点不会被遍历两次. 也可以线段树优化建边,注意优化建边跑双端队列bfs一定要把出边的权值设为1. 时间复杂度$O(nlogn)$. T2: 将所有的数排序,从大到小枚举. 每次扩展和枚举的数相同的行和列,这样扩展出的区间为矩形或L形. 在同一个区间内数的上界限制相同,而互不影响. 每个矩形或L形的方案数可以用容斥求出. 先考虑