高一上十一月上旬日记

11.1

闲话

  • \(whk\) 。补完了英语。
  • 下午大课间时 \(miaomiao\) 突然跟我们说让回班拿冲锋衣,今年就不发冬季校服了。

做题纪要

11.2

闲话

  • 上午补 \(whk\) 。补完了语文和数学。
  • 临吃午饭时突然被 \(miaomiao\) 通知吃饭时间又改成了 \(12:10\)
  • 详见 2024 NOIP 游记 11.2
  • 下午 \(miaomiao\) 又说吃饭时间调整了,早饭时间为 \(7:00\) ,午饭时间为 \(12:15\) ,晚饭时间为 \(18:15\) ,与隔壁高二的保持一致;让我们补一下没写完的 \(whk\) 作业,然后写 搜索、模拟
  • 晚上的全体奥赛生大会 \(miaomiao\) 没让我们去开,需要领奖的同学(指去年 \(NOIP\) 一等的 @K8He@jijidawang@wkh2008 )他找人代替领奖了。

做题纪要

CF372A Counting Kangaroos is Fun

  • 再次猜测答案上界在 \(O(\frac{n}{2})\) 左右,双指针扫一遍即可。

    点击查看代码
    int a[500010];
    int main()
    {
    	int n,ans=0,l,r,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	sort(a+1,a+1+n);
    	for(l=n/2,r=n;l>=1;l--)
    	{
    		if(a[r]>=a[l]*2)
    		{
    			ans++;
    			r--;
    		}
    	}
    	cout<<n-ans<<endl;
    	return 0;
    }
    

luogu P11242 碧树

  • 画图手摸下样例即可。

    点击查看代码
    int a[100010];
    int main()
    {
    	int n,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	sort(a+1,a+1+n);
    	for(i=1;i<=n;i++)
    	{
    		if(a[i]==a[n])
    		{
    			cout<<a[n]-1+(i-1)+(n-i+1)<<endl;
    			break;
    		}
    	}
    	return 0;
    }
    

luogu P5305 [GXOI/GZOI2019] 旧词

luogu P3084 [USACO13OPEN] Photo G

  • 差分约束。需要 \(SLF\) 优化 \(SPFA\) 加卡时即可。

  • 不保证正确性,被叉了属于正常现象。

    点击查看代码
    struct node
    {
    	int nxt,to,w;
    }e[600010];
    int head[600010],vis[600010],dis[600010],num[600010],cnt=0;
    void add(int u,int v,int w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void spfa(int s,int n)
    {
    	memset(vis,0,sizeof(vis));
    	memset(dis,0x3f,sizeof(dis));
    	deque<int>q;
    	dis[s]=0;
    	vis[s]=1;
    	q.push_back(s);
    	int tot=0;
    	while(q.empty()==0)
    	{
    		int x=q.front();
    		vis[x]=0;
    		q.pop_front();
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(dis[e[i].to]>dis[x]+e[i].w)
    			{
    				dis[e[i].to]=dis[x]+e[i].w;
    				num[e[i].to]=num[x]+1;
    				tot++;
    				if(num[e[i].to]>=n+1||tot>=50000000)
    				{
    					cout<<"-1"<<endl;
    					exit(0);
    				}
    				if(vis[e[i].to]==0)
    				{
    					if(q.empty()==0&&dis[e[i].to]>=dis[q.front()])
    					{
    						q.push_back(e[i].to);
    					}
    					else
    					{
    						q.push_front(e[i].to);
    					}
    					vis[e[i].to]=1;
    				}
    			}
    		}
    	}
    }
    int main()
    {
    	int n,m,l,r,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>l>>r;
    		add(l-1,r,1);
    		add(r,l-1,-1);
    	}
    	for(i=1;i<=n;i++)
    	{
    		add(i-1,i,1);
    		add(i,i-1,0);
    	}
    	spfa(0,n+1);
    	cout<<dis[n]<<endl;
    	return 0;
    }
    

11.3

闲话

  • 早上有体活,让 \(7:20\) 到机房。
  • 上午 \(7:20 \sim 11:20\) 打学校 \(OJ\) 的模拟赛。
  • 打完模拟赛后 \(field\) 突然跟我们说准备把我们都合并到一个机房去,让我们把机房里的 \(whk\) 资料都拿到厕所旁的拐角杂物间去,到杂物间后发现里面堆着“大头电脑”、洗手池和一堆乱七八搜的东西,仅剩空间且没多少了。午饭 \(field\)\(12:15\) 去吃饭。
  • 下午放 @lxyt_415x 的每日一歌《经过》。讲题时看了眼隔壁机房,在把 @5k_sync_closer 等人分走后还是不够我们坐的地方,回来后问 \(field\) 说什么时候合并机房,他说就最多两天以内,让我们做好心理准备。
  • 临吃晚饭时铃声还是 \(18:15\) 打铃,然后找 \(field\) 说把晚饭时间改了,然后又将铃声改成了正常上课的铃声。
  • 晚上让 @lxyt_415x\(field\) 申请激情演唱,然后 \(field\) 就同意了,接着我们就偷偷到机房门口偷听了, \(field\) 还称“唱得还挺好听”。
  • 详见 2024 CSP-S 游记 11.3

做题纪要

HZTG2082. 最短路

HZTG2080. 玩游戏

11.4

闲话

  • 侯操时被通知操前班呼由 \(HZ\) 精神变成了“六无六不”的前三句。
  • 早读时 \(feifei\) 过来问我们为啥来了不早读,然后让我们“激情早读”。
  • 吃完早饭回机房后 \(feifei\) 跟隔壁说今天没有模拟赛,写 字符串 。我们从门外听见后开始“欢呼雀跃”, \(miaomiao\) 随即问我们要干什么,说今天有模拟赛,他正在组。但我们跟他说昨天题还没改完,然后 \(miaomiao\) \(D\) 了下我们改题效率慢(指需要两天改一场模拟赛),然后就取消了今天的模拟赛。
  • 从学校 \(OJ\) 上得知我们打的学校 \(OJ\) 模拟赛叫“加赛”。
  • 详见 2024 CSP-S 游记 11.4
  • 临吃午饭时 \(feifei\) 跟我们说让去吃午饭的时候把电脑直接关机,下午来了后直接按照新座位表就坐,但我们没找到新座位表。
  • 下午到机房后发现座位表直接贴在了两个机房的黑板上,原合并至一个机房的计划改成了分成两个机房(据说是按照模拟赛成绩分 \(A,B\) 层)。然后放 @hh弟中弟 的每日一歌《爱的飞行日记》,由 @hh弟中弟@DanhengYinyue 激情演唱,演唱时四个教练都在听,唱的时候 \(huge\) 让我们也像听演唱会一样跟着唱。然后 \(miaomiao,feifei\) 收集了下坏掉的键盘和鼠标。
  • 现在左边是 @lxyt_415x ,右边是 @luobotianle
  • 晚上 @lxyt_415x 哼歌被 \(feifei\) \(D\) 了。

做题纪要

luogu P4899 [IOI2018] werewolf 狼人

  • 翻译过来就是询问是否存在一条从 \(s\)\(e\) 的路径使得先只经过编号 \(\ge l\) 的点,再只经过编号 \(\le r\) 的点。

  • 考虑建两棵 \(Kruskal\) 重构树。第一棵以每条边端点编号的较小值为边权建立重构树(最大生成树),第二棵以每条边端点编号的较大值为边权建立重构树(最小生成树)。

  • 新建出的虚点的点权自然要用边权代替。然后就可以在第一棵重构树上倍增找到点权 \(\ge l\) 的最浅的节点,在第二棵重构树上倍增找到点权 \(\le r\) 的最浅的节点。这两个节点子树内部所有原图中的点就是可行路径上的点。

  • 接着需要判断是否有可行的中转点同时在两棵子树内部,主席树维护二维数点判交集即可。

  • 具体地,以第一棵子树的 \(DFS\) 序为下标,以第二棵子树的 \(DFS\) 序为权值插入主席树即可。

    点击查看代码
    int p[400010];
    pair<int,int>E[400010];
    bool cmp1(pair<int,int> a,pair<int,int>b)
    {
        return min(a.first,a.second)>min(b.first,b.second);
    }
    bool cmp2(pair<int,int> a,pair<int,int>b)
    {
        return max(a.first,a.second)<max(b.first,b.second);
    }
    struct DSU
    {
        int fa[400010];
        void init(int n)
        {
            for(int i=1;i<=n;i++)
            {
                fa[i]=i;
            }
        }
        int find(int x)
        {
            return fa[x]==x?x:fa[x]=find(fa[x]);
        }
    }D;
    struct Gragh
    {
        struct node
        {
            int nxt,to;
        }e[800010];
        int head[400010],fa[400010][25],dfn[400010],out[400010],c[400010],cnt=0,tim=0;
        void add(int u,int v)
        {
            cnt++;
            e[cnt].nxt=head[u];
            e[cnt].to=v;
            head[u]=cnt;
        }
        void dfs(int x,int father)
        {	
            tim++;
            dfn[x]=tim;
            fa[x][0]=father;
            for(int i=1;i<=20;i++)
            {
                fa[x][i]=fa[fa[x][i-1]][i-1];
            }
            for(int i=head[x];i!=0;i=e[i].nxt)
            {
                if(e[i].to!=father)
                {
                    dfs(e[i].to,x);
                }
            }
            out[x]=tim;
        }
        void kruskal(int n,int m,int pd)
        {
            D.init(2*n);
            if(pd==1)
            {
                sort(E+1,E+1+m,cmp1);
            }
            else
            {
                sort(E+1,E+1+m,cmp2);
            }
            for(int i=1,tot=n;i<=m&&tot<=2*n-1;i++)
            {
                int x=D.find(E[i].first),y=D.find(E[i].second);
                if(x!=y)
                {
                    tot++;
                    if(pd==1)
                    {
                        c[tot]=min(E[i].first,E[i].second);
                    }
                    else
                    {
                        c[tot]=max(E[i].first,E[i].second);
                    }
                    D.fa[x]=D.fa[y]=tot;
                    add(tot,x);
                    add(tot,y);
                }
            }
        }
        int ask(int x,int val,int pd)
        {   
            int rt=x;
            if(pd==1)
            {
                for(int i=20;i>=0;i--)
                {
                    if(fa[rt][i]!=0&&c[fa[rt][i]]>=val)
                    {
                        rt=fa[rt][i];
                    }
                }
            }
            else
            {
                for(int i=20;i>=0;i--)
                {
                    if(fa[rt][i]!=0&&c[fa[rt][i]]<=val)
                    {
                        rt=fa[rt][i];
                    }
                }
            }
            return rt;
        }
    }A,B;
    struct PDS_SMT
    {
        int root[400010],rt_sum;
        struct SegmentTree
        {
            int ls,rs,sum;
        }tree[400010<<5];
        #define lson(rt) (tree[rt].ls)
        #define rson(rt) (tree[rt].rs)
        int build_rt()
        {
            rt_sum++;
            lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0;
            return rt_sum;
        }
        void update(int pre,int &rt,int l,int r,int pos,int val)
        {
            rt=build_rt();
            tree[rt]=tree[pre];
            tree[rt].sum=tree[pre].sum+val;
            if(l==r)
            {
                return;
            }
            int mid=(l+r)/2;
            if(pos<=mid)
            {
                update(lson(pre),lson(rt),l,mid,pos,val);
            }
            else
            {
                update(rson(pre),rson(rt),mid+1,r,pos,val);
            }
        }
        int query(int rt1,int rt2,int l,int r,int x,int y)
        {
            if(x<=l&&r<=y)
            {
                return tree[rt2].sum-tree[rt1].sum;
            }
            int mid=(l+r)/2,ans=0;
            if(x<=mid)
            {
                ans+=query(lson(rt1),lson(rt2),l,mid,x,y);
            }
            if(y>mid)
            {
                ans+=query(rson(rt1),rson(rt2),mid+1,r,x,y);
            }
            return ans;
        }
    }T;
    bool cmp3(int a,int b)
    {
        return A.dfn[a]<A.dfn[b];
    }
    int main()
    {
        int n,m,q,s,e,l,r,x,y,i;
        cin>>n>>m>>q;
        for(i=1;i<=m;i++)
        {
            cin>>E[i].first>>E[i].second;
            E[i].first++;
            E[i].second++;
        }
        A.kruskal(n,m,1);
        B.kruskal(n,m,2);
        A.dfs(2*n-1,0);
        B.dfs(2*n-1,0);
        for(i=1;i<=2*n-1;i++)
        {
            p[i]=i;
        }
        sort(p+1,p+1+2*n-1,cmp3);
        for(i=1;i<=2*n-1;i++)
        {
            T.update(T.root[i-1],T.root[i],1,2*n-1,B.dfn[p[i]],p[i]<=n);
        }
        for(i=1;i<=q;i++)
        {
            cin>>s>>e>>l>>r;
            s++;
            e++;
            l++;
            r++;
            x=A.ask(s,l,1);
            y=B.ask(e,r,2);
            cout<<(T.query(T.root[A.dfn[x]-1],T.root[A.out[x]],1,2*n-1,B.dfn[y],B.out[y])!=0)<<endl;
        }
        return 0;
    }
    

LibreOJ 2865. 「IOI2018」狼人

  • 改成交互即可。

  • 返回 int 类型的数组和获取 \(m,q\) 是难处理的,不妨用 vector 代替。

    点击查看代码
    #include"werewolf.h"
    int p[400010];
    pair<int,int>E[400010];
    vector<int>ans;
    bool cmp1(pair<int,int> a,pair<int,int>b)
    {
        return min(a.first,a.second)>min(b.first,b.second);
    }
    bool cmp2(pair<int,int> a,pair<int,int>b)
    {
        return max(a.first,a.second)<max(b.first,b.second);
    }
    struct DSU
    {
        int fa[400010];
        void init(int n)
        {
            for(int i=1;i<=n;i++)
            {
                fa[i]=i;
            }
        }
        int find(int x)
        {
            return fa[x]==x?x:fa[x]=find(fa[x]);
        }
    }D;
    struct Gragh
    {
        struct node
        {
            int nxt,to;
        }e[800010];
        int head[400010],fa[400010][25],dfn[400010],out[400010],c[400010],cnt=0,tim=0;
        void add(int u,int v)
        {
            cnt++;
            e[cnt].nxt=head[u];
            e[cnt].to=v;
            head[u]=cnt;
        }
        void dfs(int x,int father)
        {	
            tim++;
            dfn[x]=tim;
            fa[x][0]=father;
            for(int i=1;i<=20;i++)
            {
                fa[x][i]=fa[fa[x][i-1]][i-1];
            }
            for(int i=head[x];i!=0;i=e[i].nxt)
            {
                if(e[i].to!=father)
                {
                    dfs(e[i].to,x);
                }
            }
            out[x]=tim;
        }
        void kruskal(int n,int m,int pd)
        {
            D.init(2*n);
            if(pd==1)
            {
                sort(E+1,E+1+m,cmp1);
            }
            else
            {
                sort(E+1,E+1+m,cmp2);
            }
            for(int i=1,tot=n;i<=m&&tot<=2*n-1;i++)
            {
                int x=D.find(E[i].first),y=D.find(E[i].second);
                if(x!=y)
                {
                    tot++;
                    if(pd==1)
                    {
                        c[tot]=min(E[i].first,E[i].second);
                    }
                    else
                    {
                        c[tot]=max(E[i].first,E[i].second);
                    }
                    D.fa[x]=D.fa[y]=tot;
                    add(tot,x);
                    add(tot,y);
                }
            }
        }
        int ask(int x,int val,int pd)
        {   
            int rt=x;
            if(pd==1)
            {
                for(int i=20;i>=0;i--)
                {
                    if(fa[rt][i]!=0&&c[fa[rt][i]]>=val)
                    {
                        rt=fa[rt][i];
                    }
                }
            }
            else
            {
                for(int i=20;i>=0;i--)
                {
                    if(fa[rt][i]!=0&&c[fa[rt][i]]<=val)
                    {
                        rt=fa[rt][i];
                    }
                }
            }
            return rt;
        }
    }A,B;
    struct PDS_SMT
    {
        int root[400010],rt_sum;
        struct SegmentTree
        {
            int ls,rs,sum;
        }tree[400010<<5];
        #define lson(rt) (tree[rt].ls)
        #define rson(rt) (tree[rt].rs)
        int build_rt()
        {
            rt_sum++;
            lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0;
            return rt_sum;
        }
        void update(int pre,int &rt,int l,int r,int pos,int val)
        {
            rt=build_rt();
            tree[rt]=tree[pre];
            tree[rt].sum=tree[pre].sum+val;
            if(l==r)
            {
                return;
            }
            int mid=(l+r)/2;
            if(pos<=mid)
            {
                update(lson(pre),lson(rt),l,mid,pos,val);
            }
            else
            {
                update(rson(pre),rson(rt),mid+1,r,pos,val);
            }
        }
        int query(int rt1,int rt2,int l,int r,int x,int y)
        {
            if(x<=l&&r<=y)
            {
                return tree[rt2].sum-tree[rt1].sum;
            }
            int mid=(l+r)/2,ans=0;
            if(x<=mid)
            {
                ans+=query(lson(rt1),lson(rt2),l,mid,x,y);
            }
            if(y>mid)
            {
                ans+=query(rson(rt1),rson(rt2),mid+1,r,x,y);
            }
            return ans;
        }
    }T;
    bool cmp3(int a,int b)
    {
        return A.dfn[a]<A.dfn[b];
    }
    vector<int> check_validity(int n,vector<int>u,vector<int>v,vector<int>s,vector<int>e,vector<int>l,vector<int>r)
    {
        for(int i=0;i<=u.size()-1;i++)
        {
            E[i+1]=make_pair(u[i]+1,v[i]+1);
        }
        A.kruskal(n,u.size(),1);
        B.kruskal(n,u.size(),2);
        A.dfs(2*n-1,0);
        B.dfs(2*n-1,0);
        for(int i=1;i<=2*n-1;i++)
        {
            p[i]=i;
        }
        sort(p+1,p+1+2*n-1,cmp3);
        for(int i=1;i<=2*n-1;i++)
        {
            T.update(T.root[i-1],T.root[i],1,2*n-1,B.dfn[p[i]],p[i]<=n);
        }
        for(int i=0;i<=s.size()-1;i++)
        {
            int x=A.ask(s[i]+1,l[i]+1,1);
            int y=B.ask(e[i]+1,r[i]+1,2);
            ans.push_back(T.query(T.root[A.dfn[x]-1],T.root[A.out[x]],1,2*n-1,B.dfn[y],B.out[y])!=0);
        }
        return ans;
    }
    

HZTG2081. 排列

luogu P2491 [SDOI2011] 消防

HZTG2083. 矩形

11.5

闲话

  • 早读 \(huge\) 让站到 \(6:40\) 再坐下。
  • 上午 \(7:25 \sim 11:25\) 打 accoders NOI 的模拟赛。
  • 中午下课时众人正打算坐电梯下去结果电梯到了后 \(miaomiao\) 从里面出来了,我们遂跑到了四楼坐电梯。
  • 下午因为高二的信息课和体育课连上了,遂没放每日一歌。讲题的时候 \(feifei\) 突然说我们如果有不一样的解法私下讨论就行,但我们之前都是直接开麦一起讲的。
  • 详见 2024 CSP-S 游记 11.5
  • 临吃晚饭时被 \(feifei\) 通知让我和 @lhx 换下位置,可能是因为我和 @lxyt_415x 魔怔太多被他发现了(?)。现在左边是 @Pursuing_OIer ,右边是 @HANGRY_sol
  • 晚上 \(huge\) 来了后说了下高二的宿舍内务通报和违纪情况,说这次他救不了违纪的人,而且自从他上次和年级部闹翻后好像年级部查得更严了(指都开始查样被了); \(huge\) 还用 \(ChatGPT\) 新给了他们一个口号,说我们大部分可能文采不太好,上次 @zhengchenxi414 写的宣誓词还不错但不能光让一个人写,让我们好好利用自己手头的资源、工具,加强彼此的合作; \(huge\) 说今天题的难度跟 \(CSP-S\) 难度接近,要是把这套题放在考前打就更好了。
  • 详见 2024 NOIP 游记 11.5

做题纪要

luogu P4655 [CEOI2017] Building Bridges

  • \(f_{i}\) 表示将 \(1\)\(i\) 根柱子连接的最小花费,状态转移方程为 \(f_{i}=\min\limits_{j=1}^{i-1}\{ f_{j}+(h_{i}-h_{j})^{2}+\sum\limits_{k=j+1}^{i-1}w_{k} \}\) ,边界为 \(f_{1}=0\)

  • \(sum_{i}=\sum\limits_{j=1}^{i}w_{j}\) ,并拆掉上式的 \(\min\)\(\begin{cases} x=h_{j} \\ y=f_{j}+h_{j}^{2}-sum_{j} \\ k=2h_{i} \\ b=f_{i}-h_{i}^{2}-sum_{i-1} \end{cases}\) ,其中横坐标 \(x\) 和斜率 \(k\) 都不是单调递增的。

  • 仍考虑维护下凸壳,但难点在于如何插入。

  • 平衡树维护动态插入的代码过于繁琐,考虑使用 \(CDQ\) 分治维护。

    • cdq(l,mid) 计算 \(f_{i}(i \in [l,mid])\) ,接着按照 \([l,mid]\) 这个区间内的决策点建凸壳(建凸壳前需要先按照 \(x,y\) 为第一、二关键字排序使其单调),并利用这个凸壳更新 \(f_{i}(i \in [mid+1,r])\)
      • 提供一组 \(hack\) 数据。

        • 随便随机几组值域较小的数据就出来了。
        点击查看 hack 数据
        in:
        3
        1 4 8 
        4 3 9 
        ans:
        25
        
    • 更新时因为决策点已经固定了,就不再需要像普通 \(DP\) 一样动态插入决策点了,遂可以先将 \([mid+1,r]\) 中的先按照对应的 \(k\) 排序再使用单调队列更新,也可以直接二分求解。前后两种写法分别对应 luogu P10979 任务安排 2luogu P5785 [SDOI2012] 任务安排
      • 先按照 \(k\) 排序的做法可以一开始就排好序 \(CDQ\) 分治过程中按照和 \(mid\) 的大小关系分组。
    • 删除 \([l,mid]\) 中的决策点后就可以继续递归处理下一步,即 cdq(mid+1,r)
    点击查看代码
    ll f[200010],h[200010],w[200010],k[200010],sum[200010],p[200010],tmp[200010];
    deque<ll>q;
    bool cmpk(ll a,ll b)
    {
        return k[a]<k[b];
    }
    ll x(ll i)
    {
        return h[i];
    }
    ll y(ll i)
    {
        return f[i]+h[i]*h[i]-sum[i];
    }
    bool cmpx(ll a,ll b)
    {
        return (x(a)==x(b))?(y(a)<y(b)):(x(a)<x(b));
    }
    void cdq(ll l,ll r)
    {
        if(l==r)
        {
            return;
        }
        ll mid=(l+r)/2;
        for(ll i=l,x=l,y=mid+1;i<=r;i++)
        {
            if(p[i]<=mid)
            {
                tmp[x]=p[i];
                x++;
            }
            else
            {
                tmp[y]=p[i];
                y++;
            }
        }
        for(ll i=l;i<=r;i++)
        {
            p[i]=tmp[i];
        }
        cdq(l,mid);
        q.clear();
        for(ll i=l;i<=mid;i++)
        {
            while(q.size()>=2&&(y(q.back())-y(q[q.size()-2]))*(x(p[i])-x(q.back()))>=(y(p[i])-y(q.back()))*(x(q.back())-x(q[q.size()-2])))
            {
                q.pop_back();
            }
            q.push_back(p[i]);
        }
        for(ll i=mid+1;i<=r;i++)
        {
            while(q.size()>=2&&(y(q[1])-y(q.front()))<=k[p[i]]*(x(q[1])-x(q.front())))
            {
                q.pop_front();
            }
            f[p[i]]=min(f[p[i]],f[q.front()]+(h[p[i]]-h[q.front()])*(h[p[i]]-h[q.front()])+sum[p[i]-1]-sum[q.front()]);
        }
        cdq(mid+1,r);
        sort(p+l,p+r+1,cmpx);
    }
    int main()
    {
        ll n,i;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>h[i];
            k[i]=h[i]*2;
            p[i]=i;
        }
        for(i=1;i<=n;i++)
        {
            cin>>w[i];
            sum[i]=sum[i-1]+w[i];
        }
        sort(p+1,p+1+n,cmpk);
        memset(f,0x3f,sizeof(f));
        f[1]=0;
        cdq(1,n);
        cout<<f[n]<<endl;
        return 0;
    }
    

P438. 选彩笔(rgb)

P439. 兵蚁排序(sort)

luogu P4116 Qtree3

  • 多倍经验: luogu P4092 [HEOI2016/TJOI2016] 树

  • 把编号挂在 \(DFS\) 序上,然后对 \(DFS\) 序修改即可。

  • 查询时取 \(DFS\) 序最小的即可。

    点击查看代码
    struct node
    {
        int nxt,to;
    }e[200010];
    int head[200010],dfn[200010],siz[200010],dep[200010],fa[200010],son[200010],top[200010],col[200010],pos[200010],cnt=0,tot=0;
    void add(int u,int v)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    void dfs1(int x,int father)
    {
        siz[x]=1;
        fa[x]=father;
        dep[x]=dep[father]+1;
        for(int i=head[x];i!=0;i=e[i].nxt)
        {
            if(e[i].to!=father)
            {
                dfs1(e[i].to,x);
                siz[x]+=siz[e[i].to];
                son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x];
            }
        }
    }
    void dfs2(int x,int id)
    {
        top[x]=id;
        tot++;
        dfn[x]=tot;
        pos[tot]=x;
        if(son[x]!=0)
        {
            dfs2(son[x],id);
            for(int i=head[x];i!=0;i=e[i].nxt)
            {
                if(e[i].to!=fa[x]&&e[i].to!=son[x])
                {
                    dfs2(e[i].to,e[i].to);
                }
            }
        }
    }
    struct SMT
    {
        struct SegmentTree
        {
            int minn;
        }tree[400010];
        int lson(int x)
        {
            return x*2;
        }
        int rson(int x)
        {
            return x*2+1;
        }
        void pushup(int rt)
        {
            tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn);
        }
        void build(int rt,int l,int r)
        {
            if(l==r)
            {
                tree[rt].minn=0x3f3f3f3f;
                return;
            }
            int mid=(l+r)/2;
            build(lson(rt),l,mid);
            build(rson(rt),mid+1,r);
            pushup(rt);
        }
        void update(int rt,int l,int r,int pos,int val)
        {
            if(l==r)
            {
                tree[rt].minn=val;
                return;
            }
            int mid=(l+r)/2;
            if(pos<=mid)
            {
                update(lson(rt),l,mid,pos,val);
            }
            else
            {
                update(rson(rt),mid+1,r,pos,val);
            }
            pushup(rt);
        }
        int query(int rt,int l,int r,int x,int y)
        {
            if(x<=l&&r<=y)
            {
                return tree[rt].minn;
            }
            int mid=(l+r)/2,ans=0x3f3f3f3f;
            if(x<=mid)
            {
                ans=min(ans,query(lson(rt),l,mid,x,y));
            }
            if(y>mid)
            {
                ans=min(ans,query(rson(rt),mid+1,r,x,y));
            }
            return ans;
        }
    }T;
    void update1(int x,int n)
    {
        col[x]^=1;
        T.update(1,1,n,dfn[x],(col[x]==1)?dfn[x]:0x3f3f3f3f);
    }
    int query1(int x,int n)
    {
        int ans=0x3f3f3f3f;
        while(top[x]!=1)
        {
            ans=min(ans,T.query(1,1,n,dfn[top[x]],dfn[x]));
            x=fa[top[x]];
        }
        ans=min(ans,T.query(1,1,n,dfn[1],dfn[x]));
        return (ans==0x3f3f3f3f)?-1:pos[ans];
    }
    int main()
    {
        int n,q,u,v,i;
        cin>>n>>q;
        for(i=1;i<=n-1;i++)	
        {
            cin>>u>>v;
            add(u,v);
            add(v,u);
        }
        dfs1(1,0);
        dfs2(1,1);
        T.build(1,1,n);
        for(i=1;i<=q;i++)
        {
            cin>>u>>v;
            if(u==0)
            {
                update1(v,n);
            }
            else
            {
                cout<<query1(v,n)<<endl;
            }
        }
        return 0;
    }
    

P440. 人口局 DBA(dba)

11.6

闲话

  • 吃完早饭后 \(miaomiao\) 过来问昨天晚上 \(huge\) 说的宿舍违纪的人去年级部了吗,然后开始尝试“搜刮”零食。
  • 上午 \(7:30 \sim 11:30\) 打学校 \(OJ\) 的模拟赛。
  • 下午放 @5k_sync_closer 的每日一歌《蜂鸟》,由 @5k_sync_closer@K8He 激情演唱。然后讲题。
  • \(huge\) 找人来把前几天拿来的挂钩钉在了墙上让我们挂衣服用, \(feifei\) 称材料有点劣质。
  • \(17:40\)\(huge\) 进来问我们怎么讨论时间都在自己做题,做题时间都在各自讨论,这不是有铃声提醒吗。我说我们怎么知道是讨论铃声还是做题铃声,然后 \(huge\) 说这是个好问题,就开始 \(D\) 我们不认真看时间表,记不住时间,然后就扯到了高二那边宿舍违纪和早操情况。

做题纪要

HZTG5733. 新的阶乘

HZTG5734. 博弈树

HZTG5735. 划分

luogu P4360 [CEOI2004] 锯木厂选址

  • \(f_{i}\) 表示把第二个锯木厂建立在第 \(i\) 棵树时的最小花费,状态转移方程为 \(f_{i}=\min\limits_{j=1}^{i-1}\{ \sum\limits_{k=1}^{j}\sum\limits_{h=k}^{j}w _{k}d_{h}+\sum\limits_{k=j+1}^{i}\sum\limits_{h=k}^{i}w_{k}d_{h}+\sum\limits_{k=i+1}^{n}\sum\limits_{h=k}^{n}w_{k}d_{h} \}\)

  • 考虑费用提前计算,设 \(dis_{i}=\sum\limits_{j=i}^{n}d_{j},sum_{i}=\sum\limits_{j=1}^{i}w_{i}\) ,上式等价于 \(f_{i}=\min\limits_{j=1}^{i-1}\{\sum\limits_{k=1}^{n}w_{k}dis_{k}-sum_{j} \times dis_{j}-dis_{i} \times (sum_{i}-sum_{j}) \}=\sum\limits_{k=1}^{n}w_{k}dis_{k}-dis_{i} \times sum_{i}+\max\limits_{j=1}^{i-1}\{sum_{j} \times dis_{j}-dis_{i} \times sum_{j} \}\)

  • 拆掉 \(\max\) ,有 \(\begin{cases} x=sum_{j} \\ y=sum_{j} \times dis_{j} \\ k=dis_{i} \\ b=f_{i}-\sum\limits_{k=1}^{n}w_{k}dis_{k}+dis_{i} \times sum_{i} \end{cases}\) ,其中横坐标 \(x\) 是单调递增的,斜率 \(k\) 是单调递减的。

  • 单调队列维护上凸壳即可。

    点击查看代码
    ll w[200010],d[200010],sum[200010],dis[200010];
    deque<ll>q;
    ll x(ll i)
    {
        return sum[i];
    }
    ll y(ll i)
    {
        return sum[i]*dis[i];
    }
    int main()
    {
        ll n,num=0,ans=0x7f7f7f7f,i;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>w[i]>>d[i];
            sum[i]=sum[i-1]+w[i];
        }
        for(i=n;i>=1;i--)
        {
            dis[i]=dis[i+1]+d[i];
            num+=dis[i]*w[i];
        }
        q.push_back(1);
        for(i=2;i<=n;i++)
        {
            while(q.size()>=2&&(y(q[1])-y(q.front()))>=(x(q[1])-x(q.front()))*dis[i])
            {
                q.pop_front();
            }
            ans=min(ans,num-sum[q.front()]*dis[q.front()]-dis[i]*(sum[i]-sum[q.front()]));
            while(q.size()>=2&&(y(q.back())-y(q[q.size()-2]))*(x(i)-x(q.back()))<=(y(i)-y(q.back()))*(x(q.back())-x(q[q.size()-2])))
            {
                q.pop_back();
            }
            q.push_back(i);
        }
        cout<<ans<<endl;
        return 0;
    }
    

luogu P4072 [SDOI2016] 征途

  • \(s^{2}=\overline{x^{2}}-\overline{x}^{2}\) 比较显然;提前 \(m\) 乘进去避免精度误差;把距离挂在点上,不妨钦定第 \(m\) 天在 \(n\) 地过夜;考虑只统计平方的贡献

  • \(f_{i,j}\) 表示第 \(i\) 天在 \(j\) 地过夜的最小代价,状态转移方程为 \(f_{i,j}=\min\limits_{k=i-1}^{j-1}\{ f_{i-1,k}+(\sum\limits_{h=k+1}^{j}d_{h})^{2} \}\)

  • \(sum_{i}=\sum\limits_{j=1}^{i}d_{j}\) ,上式等价于 \(f_{i,j}=\min\limits_{k=i-1}^{j-1}\{ f_{i-1,k}+(sum_{j}-sum_{k})^{2} \}=\min\limits_{k=i-1}^{j-1}\{ f_{i-1,k}+sum_{j}^{2}+sum_{k}^{2}-2sum_{j} \times sum_{k} \}\)

  • 拆掉 \(\min\) ,有 \(\begin{cases} x=sum_{k} \\ y=f_{i-1,k}+sum_{k}^{2} \\ k=2sum_{j} \\ b=f_{i,j}-sum_{j}^{2} \end{cases}\) ,其中横坐标 \(x\) 和斜率 \(k\) 是单调递增的。

  • 对于每个 \(i\) 分别单调队列维护下凸壳即可。

  • 最终有 \(mf_{m,n}-sum_{n} \times sum_{n}\) 即为所求。

    点击查看代码
    ll d[3010],sum[3010],f[2][3010];
    deque<ll>q;
    ll x(ll k)
    {
        return sum[k];
    }
    ll y(ll i,ll k)
    {
        return f[i&1][k]+sum[k]*sum[k];
    }
    int main()
    {
        ll n,m,i,j;
        cin>>n>>m;
        for(i=1;i<=n;i++)
        {
            cin>>d[i];
            sum[i]=sum[i-1]+d[i];
        }
        memset(f,0x3f,sizeof(f));
        for(i=1;i<=n;i++)
        {
            f[1][i]=sum[i]*sum[i];
        }
        for(i=2;i<=m;i++)
        {
            q.clear();
            q.push_back(i-1);
            for(j=i;j<=n;j++)
            {
                while(q.size()>=2&&(y(i-1,q[1])-y(i-1,q.front()))<=(x(q[1])-x(q.front()))*2*sum[j])
                {
                    q.pop_front();
                }
                f[i&1][j]=f[(i-1)&1][q.front()]+(sum[j]-sum[q.front()])*(sum[j]-sum[q.front()]);
                while(q.size()>=2&&(y(i-1,q.back())-y(i-1,q[q.size()-2]))*(x(j)-x(q.back()))>=(y(i-1,j)-y(i-1,q.back()))*(x(q.back())-x(q[q.size()-2])))
                {
                    q.pop_back();
                }
                q.push_back(j);
            }
        }
        cout<<m*f[m&1][n]-sum[n]*sum[n]<<endl;
        return 0;
    }
    

11.7

闲话

  • 上午 \(7:25 \sim 11:55\) 打 accoders NOI 的模拟赛。
  • 下午 \(huge\) 强调了下机房讨论、宿舍泡面(自己保管好,别被查到就行)、个人卫生问题; \(huge\) 看见 @CuFeO4 的冲锋衣后说 \(HZ\) 终于给我们做件像样的校服了。
  • 吃完晚饭后 @CTHoi\(505\) 机房外门把手弄折了(我之前开就感觉门把手有点松了,没想到真能弄下来), \(miaomiao\) 来了后把涉及到的人员交出去处理了一下,然后就回来 \(D\) 我们这两届是他见过的习惯最差的, \(D\) 我们 \(CSP\) 考得分太低了,高一的还好,高二的不好好干事还成天捣蛋、故意把东西用坏。上次 9.20 时他好不容易给个高一的请下假来复习初赛,然后就因为按电梯这事就把高一的都赶回去了,要是他是高二这届的主教练,他早就让他们走了。
  • 晚上听多校的讲题。

做题纪要

P470. 图书管理

P472. 函数

P471. 两棵树

luogu P2480 [SDOI2010] 古代猪文

  • 等价于求 \(g^{\sum\limits_{d|n}\binom{n}{\frac{n}{d}}} \bmod 999911659=g^{\sum\limits_{d|n}\binom{n}{d}} \bmod 999911659\)

  • 观察到指数较大且 \(999911659\) 是质数,考虑欧拉定理。

  • \(g \ne 999911659\) 时等价于求 \(g^{\sum\limits_{d|n}\binom{n}{d} \bmod 999911658} \bmod 999911659\)

  • 直接跑 \(exLucas\) 显然可以做,但考虑更加简便的做法。

  • \(999911658\) 进行质因数分解后可以得到 \(999911658=2 \times 3 \times 4679 \times 35617\) ,分别 \(Lucas\) 计算后用 \(CRT\) 合并即可。

    点击查看代码
    const ll p=999911659,m[5]={0,2,3,4679,35617};
    ll a[5];
    ll qpow(ll a,ll b,ll p)
    {
        ll ans=1;
        while(b)
        {
            if(b&1)
            {
                ans=ans*a%p;
            }
            b>>=1;
            a=a*a%p;
        }
        return ans;
    }
    ll C(ll n,ll m,ll p)
    {
        if(n>=m&&n>=0&&m>=0)
        {
            ll up=1,down=1;
            for(ll i=n-m+1;i<=n;i++)
            {
                up=up*i%p;
            }
            for(ll i=1;i<=m;i++)
            {
                down=down*i%p;
            }
            return up*qpow(down,p-2,p)%p;
        }
        else
        {
            return 0;
        }
    }
    ll lucas(ll n,ll m,ll p)
    {
        return (n>=m&&n>=0&&m>=0)?(m?C(n%p,m%p,p)*lucas(n/p,m/p,p)%p:1):0;
    }
    void exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(b==0)
        {
            x=1;
            y=0;
        }
        else
        {
            exgcd(b,a%b,y,x);
            y-=a/b*x;
        }
    }
    ll inv(ll a,ll p)
    {
        ll x=0,y=0;
        exgcd(a,p,x,y);
        return (x%p+p)%p;
    }
    ll crt(ll n)
    {
        ll mul=1,r,ans=0;
        for(ll i=1;i<=n;i++)
        {
            mul*=m[i];
        }
        for(ll i=1;i<=n;i++)
        {
            r=mul/m[i];
            ans=(ans+(a[i]*r%mul)*inv(r,m[i])%mul)%mul;
        }
        return ans;
    }
    int main()
    {
        ll n,g,i,d;
        cin>>n>>g;
        if(g%p==0)
        {
            cout<<0<<endl;
        }
        else
        {
            for(i=1;i<=4;i++)
            {
                for(d=1;d<=sqrt(n);d++)
                {
                    if(n%d==0)
                    {
                        if(d*d==n)
                        {
                            a[i]=(a[i]+lucas(n,d,m[i]))%m[i];
                        }
                        else
                        {
                            a[i]=(a[i]+lucas(n,d,m[i]))%m[i];
                            a[i]=(a[i]+lucas(n,n/d,m[i]))%m[i];
                        }
                    }
                }
            }
            cout<<qpow(g,crt(4),p)<<endl;
        }
        return 0;
    }
    

luogu P7984 [USACO21DEC] Tickets P

  • 考虑对票建虚点,从 \(c_{i}\)\(i+n\) 连一条权值为 \(p_{i}\) 的边,然后从 \(i+n\)\([a_{i},b_{i}]\) 连一条权值为 \(0\) 的边。

  • 建出反图后 \(1 \to i\)\(n \to i\) 的路径集合会有重复统计的部分,不妨以 \(dis_{1,i}+dis_{n,i}\) 作为初始值然后再进行一遍松弛操作(若没有重复部分就不需要松弛了)。

  • 然后就是线段树优化建图板子了。

    点击查看代码
    struct SMT_Q_BG
    {
        ll id[900010],dis[2][900010],d[900010],vis[900010];
        vector<pair<ll,ll> >e[900010];
        ll lson(ll x)
        {
            return x*2;
        }
        ll rson(ll x)
        {
            return x*2+1;
        }
        void build(ll rt,ll l,ll r,ll n)
        {
            e[rt+n*4].push_back(make_pair(rt,0));
            if(l==r)
            {
                id[l]=rt;
                return;
            }
            e[lson(rt)].push_back(make_pair(rt,0));
            e[rson(rt)].push_back(make_pair(rt,0));
            e[rt+n*4].push_back(make_pair(lson(rt)+n*4,0));
            e[rt+n*4].push_back(make_pair(rson(rt)+n*4,0));
            ll mid=(l+r)/2;
            build(lson(rt),l,mid,n);
            build(rson(rt),mid+1,r,n);
        }
        void update(ll rt,ll l,ll r,ll x,ll y,ll pos,ll w,ll n)
        {
            if(x<=l&&r<=y)
            {
                e[rt].push_back(make_pair(pos+n*8,w));
                return;
            }
            ll mid=(l+r)/2;
            if(x<=mid)
            {
                update(lson(rt),l,mid,x,y,pos,w,n);
            }
            if(y>mid)
            {
                update(rson(rt),mid+1,r,x,y,pos,w,n);
            }
        }
        void dijkstra1(ll s,ll id)
        {
            memset(vis,0,sizeof(vis));
            memset(dis[id],0x3f,sizeof(dis[id]));
            priority_queue<pair<ll,ll> >q;
            dis[id][s]=0;
            q.push(make_pair(-dis[id][s],s));
            while(q.empty()==0)
            {
                ll x=q.top().second;
                q.pop();
                if(vis[x]==0)
                {
                    vis[x]=1;
                    for(ll i=0;i<e[x].size();i++)
                    {
                        if(dis[id][e[x][i].first]>dis[id][x]+e[x][i].second)
                        {
                            dis[id][e[x][i].first]=dis[id][x]+e[x][i].second;
                            q.push(make_pair(-dis[id][e[x][i].first],e[x][i].first));
                        }
                    }
                }
            }
        }
        void dijkstra2(ll n,ll k)
        {
            memset(vis,0,sizeof(vis));
            priority_queue<pair<ll,ll> >q;
            for(ll i=1;i<=8*n+k;i++)
            {
                d[i]=dis[0][i]+dis[1][i];
                q.push(make_pair(-d[i],i));
            }
            while(q.empty()==0)
            {
                ll x=q.top().second;
                q.pop();
                if(vis[x]==0)
                {
                    vis[x]=1;
                    for(ll i=0;i<e[x].size();i++)
                    {
                        if(d[e[x][i].first]>d[x]+e[x][i].second)
                        {
                            d[e[x][i].first]=d[x]+e[x][i].second;
                            q.push(make_pair(-d[e[x][i].first],e[x][i].first));
                        }
                    }
                }
            }
        }
    }T;
    int main()
    {
        ll n,k,c,p,a,b,i;
        cin>>n>>k;
        T.build(1,1,n,n);
        for(i=1;i<=k;i++)
        {
            cin>>c>>p>>a>>b;
            T.e[i+n*8].push_back(make_pair(T.id[c]+n*4,p));
            T.update(1,1,n,a,b,i,0,n);
        }
        T.dijkstra1(T.id[1],0);
        T.dijkstra1(T.id[n],1);
        T.dijkstra2(n,k);
        for(i=1;i<=n;i++)
        {
            cout<<(T.d[T.id[i]]<1e15?T.d[T.id[i]]:-1)<<endl;
        }
        return 0;
    }
    

11.8

闲话

  • 侯操时级部让进行期中考试的目标宣誓,但因为上次我们就没参加(某个早预备),遂不知道誓词是啥。

做题纪要

luogu P4408 [NOI2003] 逃学的小孩

  • 题目中保证了 \(m=n-1\) ,直接套用直径端点的结论即可。

    点击查看代码
    struct node
    {
        ll nxt,to,w;
    }e[200010];
    ll head[200010],dep[200010],dis[200010],fa[200010],siz[200010],son[200010],top[200010],cnt=0,tot=0;
    void add(ll u,ll v,ll w)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        e[cnt].w=w;
        head[u]=cnt;
    }
    void dfs(ll x,ll father,ll w)
    {
        dis[x]=dis[father]+w;
        for(ll i=head[x];i!=0;i=e[i].nxt)
        {
            if(e[i].to!=father)
            {
                dfs(e[i].to,x,e[i].w);
            }
        }
    }
    void dfs1(ll x,ll father,ll w)
    {
        siz[x]=1;
        fa[x]=father;
        dep[x]=dep[father]+1;
        dis[x]=dis[father]+w;
        for(ll i=head[x];i!=0;i=e[i].nxt)
        {
            if(e[i].to!=father)
            {
                dfs1(e[i].to,x,e[i].w);
                siz[x]+=siz[e[i].to];
                son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x];
            }
        }
    }
    void dfs2(ll x,ll id)
    {	
        top[x]=id;
        if(son[x]!=0)
        {
            dfs2(son[x],id);
            for(ll i=head[x];i!=0;i=e[i].nxt)
            {
                if(e[i].to!=fa[x]&&e[i].to!=son[x])
                {
                    dfs2(e[i].to,e[i].to);
                }
            }
        }
    }
    ll lca(ll u,ll v)
    {
        while(top[u]!=top[v])
        {
            if(dep[top[u]]>dep[top[v]])
            {
                u=fa[top[u]];
            }
            else
            {
                v=fa[top[v]];
            }
        }
        return dep[u]<dep[v]?u:v;
    }
    ll get_dis(ll u,ll v)
    {
        return dis[u]+dis[v]-2*dis[lca(u,v)];
    }
    int main()
    {
        ll n,m,u,v,w,ans=0,rt1=0,rt2=0,d,i;
        cin>>n>>m;
        for(i=1;i<=n-1;i++)
        {
            cin>>u>>v>>w;
            add(u,v,w);
            add(v,u,w);
        }
        dfs(1,0,0);
        for(i=1;i<=n;i++)
        {
            rt1=(dis[i]>dis[rt1])?i:rt1;
        }
        dfs(rt1,0,0);
        for(i=1;i<=n;i++)
        {
            rt2=(dis[i]>dis[rt2])?i:rt2;
        }
        d=dis[rt2];
        dfs1(1,0,0);
        dfs2(1,1);
        for(i=1;i<=n;i++)
        {
            ans=max(ans,min(get_dis(i,rt1),get_dis(i,rt2))+d);
        }
        cout<<ans<<endl;
        return 0;
    }
    

QOJ 5312.Levenshtein Distance

luogu P4768 [NOI2018] 归程

luogu P4197 Peaks

  • 多倍经验: luogu P7834 [ONTAK2010] Peaks 加强版

  • 建立 \(Kruskal\) 重构树(最小生成树),主席树维护子树内第 \(k\) 大即可。

    点击查看代码
    struct node
    {
        int from,to,w;
    };
    int h[200010],dfn[200010],out[200010],pos[200010],c[200010],fa[200010][25],tot=0;
    vector<node>E;
    vector<int>e[200010];
    void add(int u,int v)
    {
        e[u].push_back(v);
    }
    bool cmp(node a,node b)
    {
        return a.w<b.w;
    }
    struct DSU
    {
        int fa[200010];
        void init(int n)
        {
            for(int i=1;i<=n;i++)
            {
                fa[i]=i;
            }
        }
        int find(int x)
        {
            return fa[x]==x?x:fa[x]=find(fa[x]);
        }
    }D;
    void kruskal(int n)
    {
        D.init(2*n-1);
        sort(E.begin(),E.end(),cmp);
        for(int i=0,tot=n;i<E.size()&&tot<=2*n-1;i++)
        {
            int x=D.find(E[i].from),y=D.find(E[i].to);
            if(x!=y)
            {
                tot++;
                c[tot]=E[i].w;
                D.fa[x]=D.fa[y]=tot;
                add(tot,x);
                add(tot,y);
            }
        }
    }
    void dfs(int x,int father)
    {
        tot++;
        dfn[x]=tot;
        pos[tot]=x;
        fa[x][0]=father;
        for(int i=1;i<=20;i++)
        {
            fa[x][i]=fa[fa[x][i-1]][i-1];
        }
        for(int i=0;i<e[x].size();i++)
        {
            if(e[x][i]!=father)
            {
                dfs(e[x][i],x);
            }
        }
        out[x]=tot;
    }
    int ask(int x,int val)
    {
        int rt=x;
        for(int i=20;i>=0;i--)
        {
            if(fa[rt][i]!=0&&c[fa[rt][i]]<=val)
            {
                rt=fa[rt][i];
            }
        }
        return rt;
    }
    struct PDS_SMT
    {
        int root[200010],rt_sum;
        struct SegmentTree
        {
            int ls,rs,sum;
        }tree[200010<<5];
        #define lson(rt) (tree[rt].ls)
        #define rson(rt) (tree[rt].rs)
        int build_rt()
        {
            rt_sum++;
            lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0;
            return rt_sum;
        }
        void update(int pre,int &rt,int l,int r,int pos,int val)
        {
            rt=build_rt();
            tree[rt]=tree[pre];
            tree[rt].sum+=val;
            if(l==r)
            {
                return;
            }
            int mid=(l+r)/2;
            if(pos<=mid)
            {
                update(lson(pre),lson(rt),l,mid,pos,val);
            }
            else
            {
                update(rson(pre),rson(rt),mid+1,r,pos,val);
            }
        }
        int query(int rt1,int rt2,int l,int r,int k)
        {
            if(l==r)
            {
                return (tree[rt2].sum-tree[rt1].sum>=k)?l:-1;
            }
            int mid=(l+r)/2;
            if(tree[rson(rt2)].sum-tree[rson(rt1)].sum>=k)
            {
                return query(rson(rt1),rson(rt2),mid+1,r,k);
            }
            else
            {
                return query(lson(rt1),lson(rt2),l,mid,k-(tree[rson(rt2)].sum-tree[rson(rt1)].sum));
            }
        }
    }T;
    int main()
    {
        int n,m,q,u,v,w,i;
        cin>>n>>m>>q;
        for(i=1;i<=n;i++)
        {
            cin>>h[i];
        }
        for(i=1;i<=m;i++)
        {
            cin>>u>>v>>w;
            E.push_back((node){u,v,w});
        }
        kruskal(n);
        dfs(2*n-1,0);
        for(i=1;i<=2*n-1;i++)
        {
            T.root[i]=T.root[i-1];
            if(pos[i]<=n)
            {
                T.update(T.root[i],T.root[i],1,1000000000,h[pos[i]],1);
            }
        }
        for(i=1;i<=q;i++)
        {
            cin>>u>>v>>w;
            u=ask(u,v);
            cout<<T.query(T.root[dfn[u]-1],T.root[out[u]],1,1000000000,w)<<endl;
        }
        return 0;
    }
    
    

UVA1395 苗条的生成树 Slim Span

  • 强化版: luogu P4234 最小差值生成树

    点击查看代码
    struct node
    {
        int from,to,w;
    }e[10010];
    bool cmp(node a,node b)
    {
        return a.w<b.w;
    }
    struct DSU
    {
        int fa[510];
        void init(int n)
        {
            for(int i=1;i<=n;i++)
            {
                fa[i]=i;
            }
        }
        int find(int x)
        {
            return (fa[x]==x)?x:fa[x]=find(fa[x]);
        }
    }D;
    int main()
    {
        int n,m,x,y,cnt,ans,i,j;
        while(cin>>n>>m)
        {
            if(n==0&&m==0)
            {
                break;
            }
            else
            {
                ans=0x3f3f3f3f;
                for(i=1;i<=m;i++)
                {
                    cin>>e[i].from>>e[i].to>>e[i].w;
                }
                sort(e+1,e+1+m,cmp);
                for(i=1;i<=m;i++)
                {
                    D.init(n);
                    cnt=0;
                    for(j=i;j<=m;j++)
                    {
                        x=D.find(e[j].from);
                        y=D.find(e[j].to);
                        if(x!=y)
                        {
                            D.fa[x]=y;
                            cnt++;
                            if(cnt==n-1)
                            {
                                ans=min(ans,e[j].w-e[i].w);
                                break;
                            }
                        }
                    }
                }
                cout<<((ans==0x3f3f3f3f)?-1:ans)<<endl;
            }
        }
        return 0;
    }
    

P441. 银行的源起(banking)

luogu P1709 [USACO5.5] 隐藏口令 Hidden Password

  • 最小表示法板子。

    点击查看代码
    char s[10000010];
    int main()
    {
        int n,i,l,r,len;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>s[i];
            s[n+i]=s[i];
        }
        l=1;
        r=2;
        while(l<=n&&r<=n)
        {
            len=0;
            while(len<=n-1&&s[l+len]==s[r+len])
            {
                len++;
            }
            if(len==n)
            {
                break;
            }
            else
            {
                if(s[l+len]>s[r+len])
                {
                    l+=len+1;
                    l+=(l==r);
                }
                else
                {
                    r+=len+1;
                    r+=(l==r);
                }
            }
        }
        cout<<min(l,r)-1<<endl;
        return 0;
    }
    

luogu P11253 [GDKOI2023 普及组] 小学生数学

  • \(f(i)=\frac{1}{i^{k}} \bmod p\) 是完全积性函数。

    点击查看代码
    const ll p=998244353;
    ll prime[3000010],inv[20000010],len=0;
    bool vis[20000010];
    ll qpow(ll a,ll b)
    {
        ll ans=1;
        while(b)
        {
            if(b&1)
            {
                ans=ans*a%p;
            }
            b>>=1;
            a=a*a%p;
        }
        return ans;
    }
    void isprime(ll n,ll k)
    {
        memset(vis,0,sizeof(vis));
        inv[1]=1;
        for(ll i=2;i<=n;i++)
        {
            if(vis[i]==0)
            {
                len++;
                prime[len]=i;
                inv[i]=qpow(qpow(i,k),p-2);
            }
            for(ll j=1;j<=len&&i*prime[j]<=n;j++)
            {
                vis[i*prime[j]]=1;
                inv[i*prime[j]]=inv[i]*inv[prime[j]]%p;
                if(i%prime[j]==0)
                {
                    break;
                }
            }
        }
    }
    int main()
    {
        ll n,k,ans=0,mul=1,i;
        cin>>n>>k;
        isprime(n,k);
        for(i=1;i<=n;i++)
        {
            mul=mul*i%p;
            ans=(ans+mul*inv[i]%p)%p;
        }
        cout<<ans<<endl;
        return 0;
    }
    

11.9

闲话

  • 早操时看见来参观的人了,貌似今天 \(HZ\) 又有教研活动。
  • 上午 \(7:20 \sim 11:50\) 打 accoders NOI 的模拟赛。
  • 因为模拟赛时间和高二体活时间冲突了,所以让我们中午 \(11:55\) 吃饭,下午一起补体活。
  • 下午听多校的讲题; \(feifei\) 先是被头顶的钟表掉下来砸着了腰,后来起身时又被耳机线缠住了腿,称“不要说话了啊,我今天很暴躁”。
  • 感觉 \(feifei\) 有班主任那味了。

做题纪要

P481. 星际联邦

luogu P10743 [SEERC2020] AND = OR

luogu P9488 ZHY 的生成树

  • \((u,v)\) 两点间的距离为 \(\gcd(u,v) \in [1,\min(u,v)]\)

  • 最小生成树显然是取到下界时的情况,最大生成树自然取到上界即可。

  • 具体地,从 \(u\)\(u\) 的质数倍的点连边。需要手动模拟 \(Kruskal\) 排序的过程。

    点击查看代码
    int prime[700000],len=0;
    bool vis[10000010];
    void isprime(int n)
    {
        memset(vis,0,sizeof(vis));
        for(int i=2;i<=n;i++) 
        {
            if(vis[i]==0)
            {
                len++;
                prime[len]=i;
            }
            for(int j=1;j<=len&&i*prime[j]<=n;j++)
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]==0)
                {
                    break;
                }
            }
        }
    }
    struct DSU
    {
        int fa[10000010];
        void init(int n)
        {
            for(int i=1;i<=n;i++)
            {
                fa[i]=i;
            }
        }
        int find(int x)
        {
            return fa[x]==x?x:fa[x]=find(fa[x]);
        }
    }D;
    ll kruskal(int n)
    {
        ll ans=0;
        D.init(n);
        for(int i=n/2;i>=1;i--)
        {
            for(int j=1;j<=len&&i*prime[j]<=n;j++)
            {
                int x=D.find(i),y=D.find(i*prime[j]);
                if(x!=y)
                {
                    ans+=i;
                    D.fa[x]=y;
                }
            }
        }
        return ans;
    }
    int main()
    {
        int n;
        cin>>n;
        isprime(n);
        cout<<kruskal(n)<<endl;
        return 0;
    }
    

P483. 摆烂合唱

11.10

闲话

  • 因为下周二要考期中,所以今天没有体活,让 \(6:30\) 到位。
  • 上午 \(7:30 \sim 12:00\) 打学校 \(OJ\) 的模拟赛。
  • 下午讲题。

做题纪要

CF2033B Sakurako and Water

CF2025B Binomial Coefficients, Kind Of

CF2030D QED's Favorite Permutation

luogu P11271 「Diligent-OI R1 A」DlgtPattern

  • 四个方向分讨。

    点击查看代码
    int main()
    {
        ll x,y,p,q;
        cin>>x>>y>>p>>q;
        cout<<min({p,q,x-p,y-q})<<endl;
        return 0;
    }
    

luogu P11272 「Diligent-OI R1 B」DlgtArray

  • 分讨 \(0,1\) 的个数。

    点击查看代码
    int a[1000010],sum[1000010];
    int main()
    {
        int n,q,l,r,k,i;
        scanf("%d%d",&n,&q);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(i=1;i<=q;i++)
        {
            scanf("%d%d%d",&l,&r,&k);
            if(l==r||k>=r-l+1)
            {
                if(k==0)
                {
                    cout<<0<<endl;
                }
                else
                {
                    cout<<-1<<endl;
                }
            }
            else
            {
                if(k==r-l)
                {
                    if(sum[r]-sum[l-1]==r-l+1)
                    {
                        printf("%d\n",0);
                    }
                    else
                    {
                        printf("%d\n",k-(sum[r]-sum[l-1]));
                    }
                }
                else
                {
                    printf("%d\n",abs(sum[r]-sum[l-1]-k));
                }
            }
        }
        return 0;
    }
    

luogu P11273 「Diligent-OI R1 C」DlgtRank

  • 倒着考虑即可。

    点击查看代码
    ll a[200010],b[200010],ans[200010];
    int main()
    {
        ll n,k,i;
        cin>>n>>k;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        b[0]=unique(b+1,b+1+n)-(b+1);
        for(i=b[0]-1;i>=1;i--)
        {
            ans[i]=max(0ll,ans[i+1]-(b[i+1]-b[i]-1)/k+1);
        }
        for(i=1;i<=n;i++)
        {
            cout<<ans[lower_bound(b+1,b+1+b[0],a[i])-b]<<" ";
        }
        return 0;
    }
    
posted @ 2024-11-02 15:07  hzoi_Shadow  阅读(77)  评论(3编辑  收藏  举报
扩大
缩小