首页 > 其他分享 >省选联测 43

省选联测 43

时间:2023-03-02 17:22:21浏览次数:43  
标签:head ch val 省选 43 int edge 联测 include

上午学考的时候口胡了前三题,然而 T4 看不懂样例。

树上的数

思博题。dfs 即可。容易发现每个被删掉的节点只会扫一遍。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
struct node{
    int v,next;
}edge[5000010];
int n,m,ans,last,t,head[5000010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
bool v[5000010];
void dfs(int x){
    if(v[x])return;
    v[x]=true;ans--;
    for(int i=head[x];i;i=edge[i].next)dfs(edge[i].v);
}
int main(){
    scanf("%d%d",&n,&m);ans=n;
    int a,b,f=1;scanf("%d%d",&a,&b);
    for(int i=2;i<=n;i++){
        add(f,i);
        f=((1ll*f*a+b)^19760817)%i+1;
    }
    int q,x,y;scanf("%d%d%d",&q,&x,&y);
    for(int i=1;i<=m;i++){
        dfs(q);last^=ans;
        q=(((1ll*q*x+y)^19760817)^(i+1<<1))%(n-1)+2;
    }
    printf("%d\n",last);
    return 0;
}

时代的眼泪

一眼换根。然后考虑怎么换。

首先每个节点的贡献就是子树内比它小的节点个数。根节点的答案容易得到,考虑换根时候的变化。原来的根少了一棵子树,新的根变成了全部的部分。那么每个节点记录子树内比自己小的和比父亲小的节点个数算就行了。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int read(){
    int x=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))x=10*x+ch-48,ch=getchar();
    return x;
}
int n,q,w[1000010],lsh[1000010],c[1000010];
struct node{
    int v,next;
}edge[2000010];
int t,head[2000010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
#define lowbit(x) (x&-x)
void update(int x,int val){
    while(x<=n)c[x]+=val,x+=lowbit(x);
}
int query(int x){
    int sum=0;while(x)sum+=c[x],x-=lowbit(x);return sum;
}
int val[1000010],fa[1000010];
long long dp[1000010];
void dfs1(int x,int f){
    val[x]-=query(w[x]-1);
    if(f)fa[x]-=query(w[f]-1);
    update(w[x],1);
    for(int i=head[x];i;i=edge[i].next)if(edge[i].v!=f)dfs1(edge[i].v,x);
    val[x]+=query(w[x]-1);
    if(f)fa[x]+=query(w[f]-1);
    dp[1]+=val[x];
}
void dfs2(int x,int f){
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            dp[edge[i].v]=dp[x]+query(w[edge[i].v]-1)-val[edge[i].v]-fa[edge[i].v];
            dfs2(edge[i].v,x);
        }
    }
}
int main(){
    n=read();q=read();
    for(int i=1;i<=n;i++)w[i]=read(),lsh[i]=w[i];
    sort(lsh+1,lsh+n+1);
    int cnt=unique(lsh+1,lsh+n+1)-lsh-1;
    for(int i=1;i<=n;i++)w[i]=lower_bound(lsh+1,lsh+cnt+1,w[i])-lsh;
    for(int i=1;i<n;i++){
        int u=read(),v=read();add(u,v);add(v,u);
    }
    dfs1(1,0);dfs2(1,0);
    while(q--){
        int x=read();printf("%lld\n",dp[x]);
    }
    return 0;
}

传统艺能

首先不同子序列的一个 dp 是设 \(dp_{i,j}\) 为到 \(i\) 以 \(j\) 字符结尾的方案数。转移显然:

  1. \(s_i=j\):\(dp_{i,j}=1+\sum_kdp_{i-1,k}\)。
  2. \(s_i\neq j\):\(dp_{i,j}=dp_{i-1,j}\)。
    放到区间上还带修,考虑矩阵。发现这个东西容易写成矩阵维护,那线段树写就行了。轻微卡常。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstring>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int mod=998244353;
int n,m;
char s[100010];
struct node{
    int a[5][5];
    node(){memset(a,0,sizeof(a));}
    node operator*(const node &s)const{
        node ret;
        for(int i=1;i<=4;i++)for(int j=1;j<=4;j++)for(int k=1;k<=4;k++)ret.a[i][j]=(ret.a[i][j]+1ll*a[i][k]*s.a[k][j])%mod;
        return ret;
    }
}tmp[3],tree[400010];
void pushup(int rt){
    tree[rt]=tree[lson]*tree[rson];
}
void build(int rt,int l,int r){
    if(l==r){
        tree[rt]=tmp[s[l]-'A'];return;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);build(rson,mid+1,r);
    pushup(rt);
}
void update(int rt,int L,int R,int pos,int val){
    if(L==R){
        tree[rt]=tmp[val];return;
    }
    int mid=(L+R)>>1;
    if(pos<=mid)update(lson,L,mid,pos,val);
    else update(rson,mid+1,R,pos,val);
    pushup(rt);
}
node query(int rt,int L,int R,int l,int r){
    if(l<=L&&R<=r)return tree[rt];
    int mid=(L+R)>>1;
    node val;val.a[1][1]=val.a[2][2]=val.a[3][3]=val.a[4][4]=1;
    if(l<=mid)val=val*query(lson,L,mid,l,r);
    if(mid<r)val=val*query(rson,mid+1,R,l,r);
    return val;
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n>>m>>s+1;
    for(int i=1;i<=4;i++)tmp[0].a[i][i]=tmp[1].a[i][i]=tmp[2].a[i][i]=1;
    for(int i=1;i<=4;i++)tmp[0].a[1][i]=tmp[1].a[2][i]=tmp[2].a[3][i]=1;
    build(1,1,n);
    while(m--){
        int od;cin>>od;
        if(od==1){
            int pos;char od[5];cin>>pos>>od;
            update(1,1,n,pos,od[0]-'A');
        }
        else{
            int l,r;cin>>l>>r;
            node ret=query(1,1,n,l,r);
            int ans=(1ll*ret.a[1][4]+ret.a[2][4]+ret.a[3][4])%mod;
            printf("%d\n",ans);
        }
    }
    return 0;
}

铺设道路

完全看不懂样例。

首先最少轮数就是差分数组所有正值的和。这是普及-。然后考虑怎么构造方案。

发现每次操作就相当于把差分数组 \(b\) 的 \(b_l,b_r\) 变为 \(b_l+1,b_r-1\),代价 \((r-l)^2\)。那么每次一定要 \(b_l<0,b_r>0\) 才能使得次数最少。

那么我们有一个贪心:如果值最大的话,就让 \(r\) 和最右边的 \(l\) 匹配。反之和最左边的 \(l\) 匹配。据说是对的,证明邻项交换。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstring>
#define int long long
using namespace std;
const int mod=1000000007;
int n,ans,b[300010],d[300010],tmp[300010];
int l,r,q[300010];
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&d[i]),b[i]=d[i]-d[i-1],ans+=max(b[i],0ll),tmp[i]=b[i];
    n++;b[n]=tmp[n]=-d[n-1];
    printf("%lld\n",ans);ans=0;
    l=1,r=0;
    for(int i=1;i<=n;i++){
        if(b[i]>0)q[++r]=i;
        else{
            int x=-b[i];
            while(x){
                if(b[q[r]]<=x)ans+=(i-q[r])*(i-q[r])%mod*b[q[r]]%mod,x-=b[q[r]],r--;
                else ans+=(i-q[r])*(i-q[r])%mod*x%mod,b[q[r]]-=x,x=0;
                ans%=mod;
            }
        }
    }
    printf("%lld\n",ans);ans=0;
    for(int i=1;i<=n;i++)b[i]=tmp[i];
    l=1,r=0;
    for(int i=1;i<=n;i++){
        if(b[i]>0)q[++r]=i;
        else{
            int x=-b[i];
            while(x){
                if(b[q[l]]<=x)ans+=(i-q[l])*(i-q[l])%mod*b[q[l]]%mod,x-=b[q[l]],l++;
                else ans+=(i-q[l])*(i-q[l])%mod*x%mod,b[q[l]]-=x,x=0;
                ans%=mod;
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

标签:head,ch,val,省选,43,int,edge,联测,include
From: https://www.cnblogs.com/gtm1514/p/17172522.html

相关文章

  • Modbus网关ZLAN5443D 在锂电池干燥箱的应用
    在锂离子电池生产过程中,将正负极片辊压绕卷再放入电池盒之后,须对锂电池电芯极组进行烘烤干燥。相信大家也了解水分对锂电池的性能影响是很大的,需要注液前在装配车间将锂离......
  • 刷刷刷 Day 40 | 343. 整数拆分
    343.整数拆分LeetCode题目要求给定一个正整数n,将其拆分为k个正整数的和(k>=2),并使这些整数的乘积最大化。返回你可以获得的最大乘积。示例1输入:n=2输......
  • Codeforces 438D The Child and Sequence 势能线段树
    势能线段树|拉线段树题单时发现的这道花神游历各国的骚操作至今让我印象深刻,原来有名字所谓势能,大意就是原本你在高空,操作一点下降一点,势能变少一点..当你落地时,修改......
  • error in ./src/components/NumberInfo/NumberInfo.vue?vue&type=style&index=0&id=
      删除NumberInfo中的scoped,因为使用了antdv的css变量,加了scoped导致获取不到......
  • CFR-843-Div-2解题报告
    比赛传送门C.InterestingSequence题意:给你\(n,x\),求最小的\(m\gen\),使\(n\&(n+1)\&(n+2)\&\cdots\&m=x\),或判断无解。首先每一位独立,分别考虑。与运算每一位都......
  • EDU-CFR-143解题报告
    A.TwoTowers题意:有两个积木塔,由红色/蓝色积木组成,你每次可以将一个塔的塔顶放到另一个塔上,问任意次操作后能否使得两座塔都是红蓝相间。可以将一个塔全部转移到另一......
  • Educational Codeforces Round 143 (Rated for Div
    EducationalCodeforcesRound143(RatedforDiv.2)Problem-BIdealPoint给定n个线段区间\([l,r]\),我们定义\(f(x)\)为覆盖点\(x\)的线段数,我们每次操作可以删......
  • 省选联测41,42
    省选联测41冤家路窄意义不大题,先建出最短路图,总方案减去不合法方案,枚举相遇的点和相遇的边即可,但是记得方案数都要按平方算总结:垃圾大样例夹克姥爷win了win了意义不完......
  • 6_8_天天向上_CC2430中断函数
    测试国赛硬件,软件#pragmavector=POINT_VECTOR__interruptvoidP0_IRQ(void){if(P0IFG&BUTTON_PUSH_IF_MASK){LED8^=1;P0IFG&=~BUTTON......
  • JZOJ 6664. 【2020.05.28省选模拟】最优化
    \(\text{Solution}\)原题:\(\text{HonorableMention}\)一个费用流做法,\(S\)向\(2i-1\)连流量为\(1\),费用为\(0\)的边,\(2i\)向\(T\)连流量为\(1\),费用为\(0\)......