首页 > 其他分享 >2024.10.05 刷题记录

2024.10.05 刷题记录

时间:2024-10-07 21:34:02浏览次数:8  
标签:2024.10 05 int fy tot fx tag maxn 刷题

2024.10.05 刷题记录

P7597 「EZEC-8」猜树 加强版

不难发现 \(u\) 的儿子的条件是在 \(u\) 的子树内且深度比 \(u\) 恰好大 \(1\)。

每次询问子树内的所有节点深度或许可以解决此题,但询问次数达到了 \(n^2\)。

在 \(u\) 的子树内,如果知道所属其他儿子的子树的节点,知道属于 \(u\) 子树的节点,那么可以推出剩下最后一个儿子所属子树的节点。

借用树链剖分的思想,不难发现最后一个儿子是重儿子一定是最优的。

这里可以 rand 一个 \(u\) 子树内的节点,不妨假设它属于 \(u\) 的重儿子的子树,然后对 \(u\) 的儿子依次询问距离,找出 \(u\) 的重儿子。

知道重儿子后不难找出 \(u\) 的其他节点所属的儿子的子树。

#include<bits/stdc++.h>
using namespace std;

const int maxn=5005;

int n;
int siz[maxn],hso[maxn],sontree[maxn][maxn],fa[maxn],dep[maxn];

bool vis[maxn];

inline void dfs(int u)
{
    if(!siz[u]) return ;
    int tmp=sontree[u][rand()%siz[u]+1];
    random_shuffle(sontree[u]+1,sontree[u]+1+siz[u]);
    for(int i=1;i<=siz[u];i++)
    {
        int v=sontree[u][i];
        if(dep[v]!=dep[u]+1) continue;
        int dis=0;
        if(tmp!=v) printf("? 1 %d %d\n",v,tmp),fflush(stdout),scanf("%d",&dis);
        if(dis==dep[tmp]-dep[v])
        {
            hso[u]=v;
            break;
        }
    }
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=siz[u];i++)
    {
        int v=sontree[u][i];
        if(dep[v]==dep[u]+1&&v!=hso[u])
        {
            printf("? 2 %d\n",v);fflush(stdout);
            scanf("%d",&siz[v]);siz[v]--;
            int cnt=0;
            for(int j=1;j<=siz[v]+1;j++)
            {
                int tmp;
                scanf("%d",&tmp);vis[tmp]=true;
                if(tmp==v) continue;
                sontree[v][++cnt]=tmp;
            }
        }
    }
    for(int i=1;i<=siz[u];i++)
    {
        int v=sontree[u][i];
        if(!vis[v]&&v!=hso[u]) sontree[hso[u]][++siz[hso[u]]]=v;
    }
    for(int i=1;i<=siz[u];i++)
    {
        int v=sontree[u][i];
        if(dep[v]==dep[u]+1)
        {
            fa[v]=u;
            dfs(v);
        }
    }
}

int main()
{
    srand(time(0));
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        sontree[1][++siz[1]]=i;
        printf("? 1 1 %d\n",i);fflush(stdout);
        scanf("%d",&dep[i]);
    }
    dfs(1);
    printf("! ");
    for(int i=2;i<=n;i++) printf("%d ",fa[i]);
    puts("");fflush(stdout);
}

P3469 POI2008 BLO-Blockade

线段树分治板题,线段树的区间表示这区间内节点删除的情况下都有的边,直接线段树分治即可。

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define pii pair<int,int>
#define fi first
#define se second

const int maxn=500005;

struct Edge{int u,v;}E[maxn];

int n,m;

ll cnt;
ll ans[maxn];

namespace linetree
{
    #define lch(p) p*2
    #define rch(p) p*2+1

    vector<int>ts[maxn*4];
    int f[maxn],sz[maxn];
    stack<pii>rf,rsz;
    stack<ll>rcnt;

    inline void init(){for(int i=1;i<=n;i++) f[i]=i,sz[i]=1;}
    inline int fr(int u){return f[u]==u?u:fr(f[u]);}
    inline void merge(int u,int v)
    {
        if(sz[u]>sz[v]) swap(u,v);
        rcnt.push(cnt);
        cnt-=1ll*sz[v]*sz[u];
        rf.push({u,f[u]});
        f[u]=v;
        rsz.push({v,sz[v]});
        sz[v]+=sz[u];
    }
    inline void un_do()
    {
        sz[rsz.top().fi]=rsz.top().se;
        f[rf.top().fi]=rf.top().se;
        cnt=rcnt.top();
        rsz.pop(),rf.pop(),rcnt.pop();
    }
    inline void insert(int p,int l,int r,int lx,int rx,int v)
    {
        if(rx<lx) return ;
        if(r<lx||l>rx) return ;
        if(lx<=l&&r<=rx) {ts[p].push_back(v);return ;}
        int mid=(l+r)>>1;
        insert(lch(p),l,mid,lx,rx,v),insert(rch(p),mid+1,r,lx,rx,v);
    }
    inline void solve(int p,int l,int r)
    {
        int level=rf.size();
        for(auto i:ts[p])
        {
            int u=E[i].u,v=E[i].v;
            int fu=fr(u),fv=fr(v);
            if(fu==fv) continue;
            merge(fu,fv);
        }
        if(l==r) ans[l]=cnt;
        else
        {
            int mid=(l+r)>>1;
            solve(lch(p),l,mid),solve(rch(p),mid+1,r);
        }
        while(level<rf.size()) un_do();
    }
}

int main()
{
    scanf("%d%d",&n,&m);cnt=1ll*n*(n-1)/2;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&E[i].u,&E[i].v);
        if(E[i].u>E[i].v) swap(E[i].u,E[i].v);
        linetree::insert(1,1,n,1,E[i].u-1,i);
        linetree::insert(1,1,n,E[i].u+1,E[i].v-1,i);
        linetree::insert(1,1,n,E[i].v+1,n,i);
    }
    linetree::init();
    linetree::solve(1,1,n);
    for(int i=1;i<=n;i++) printf("%lld\n",ans[i]*2);
}

P4630 APIO2018 铁人两项

看见两点间路径统计点数,考虑转为圆方树。

发现两点间经过的圆点数加上经过的方点所连接圆点数(去重,不算首尾),就是两点间可以选择的转换点个数。

将圆点附点权 \(-1\),方点附点权周围圆点的个数,统计所有点对间权值和就是总的答案。

所有点对间权值和并不难统计,本题即可在 \(O(n)\) 的时间内解决。

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const int maxn=2e5+5,maxm=4e5+5;

struct Edge
{
    int tot;
    int head[maxn*2];
    struct edgenode{int to,nxt;}edge[maxn*10];
    void add(int x,int y)
    {
        tot++;
        edge[tot].to=y;
        edge[tot].nxt=head[x];
        head[x]=tot;
    }
}T,G;

int n,m,tot,cok,sfn;
int tx[maxn*2];

ll ans;

int dfn[maxn],low[maxn];
stack<int>stk;
void tarjin(int u)
{
    sfn++;
    dfn[u]=low[u]=++cok;
    stk.push(u);
    for(int i=G.head[u];i;i=G.edge[i].nxt)
    {
        int v=G.edge[i].to;
        if(!dfn[v])
        {
            tarjin(v);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                tot++;
                tx[tot]++;
                T.add(tot,u);
                T.add(u,tot);
                int x=0;
                do{
                    x=stk.top();
                    tx[tot]++;
                    T.add(x,tot),T.add(tot,x);
                    stk.pop();
                }while(x!=v);
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}

int siz[maxn*2],w[maxn*2];
void dfs(int u,int fa)
{
    if(u<=n) w[u]=-1;
    else w[u]=tx[u];
    siz[u]=(u<=n);
    for(int i=T.head[u];i;i=T.edge[i].nxt)
    {
        int v=T.edge[i].to;
        if(v==fa) continue;
        dfs(v,u);
        ans+=2ll*siz[u]*siz[v]*w[u];
        siz[u]+=siz[v];
    }
    ans+=2ll*(sfn-siz[u])*siz[u]*w[u];
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        G.add(x,y),G.add(y,x);
    }
    tot=n;
    for(int i=1;i<=n;i++) if(!dfn[i])
    {
        sfn=0;
        tarjin(i);
        dfs(i,0);
    }
    printf("%lld",ans);
}

P3273 SCOI2011 棘手的操作

并查集加 set,每个 set 维护并查集内的最大值,每个并查集同时维护一个 \(tag\) 表示该并查集内共同加的值。

对于全局最大值,每个并查集提供一个最大值并额外维护一个全局的 set 即可。

#include<bits/stdc++.h>
using namespace std;

#define pii pair<int,int>
#define fi first
#define se second

const int maxn=3e5+5;

int n,al;
int f[maxn],a[maxn],tag[maxn];

multiset<pii>s[maxn];

inline int read()
{
    int n=0,f=1;
    char c=getchar();
    for(;c!='-'&&(c<'0'||c>'9');c=getchar());
    if(c=='-') c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar()) n=(n<<1)+(n<<3)+c-'0';
    return n*f;
}

inline int fr(int x){return f[x]==x?x:f[x]=fr(f[x]);}
inline void merge(int fx,int fy)
{
    if(s[fx].size()>s[fy].size()) swap(fx,fy);
    multiset<pii>::iterator it=s[fx].begin();
    s[0].erase({s[fx].rbegin()->fi+tag[fx],fx});
    s[0].erase({s[fy].rbegin()->fi+tag[fy],fy});
    for(;it!=s[fx].end();it++)
    {
        a[(*it).se]=(*it).fi-tag[fy]+tag[fx];
        s[fy].insert({(*it).fi-tag[fy]+tag[fx],(*it).se});
    }
    s[0].insert({s[fy].rbegin()->fi+tag[fy],fy});
    f[fx]=fy;
    s[fx].clear();tag[fx]=0;
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        // scanf("%d",&a[i]);
        a[i]=read();
        f[i]=i,s[i].insert({a[i],i});
        s[0].insert({a[i],i});
    }
    int _;
    scanf("%d",&_);
    for(int k=1;k<=_;k++)
    {
        char ch[3];int x,y;
        cin>>ch;
        if(ch[0]=='U')
        {
            scanf("%d%d",&x,&y);
            int fx=fr(x),fy=fr(y);
            if(fx==fy) continue;
            merge(fx,fy);
        }
        else if(ch[0]=='A'&&ch[1]=='1')
        {
            scanf("%d%d",&x,&y);
            int fx=fr(x);
            s[0].erase({s[fx].rbegin()->fi+tag[fx],fx});s[fx].erase({a[x],x});
            a[x]+=y;
            s[fx].insert({a[x],x});s[0].insert({s[fx].rbegin()->fi+tag[fx],fx});
        }
        else if(ch[0]=='A'&&ch[1]=='2')
        {
            scanf("%d%d",&x,&y);
            int fx=fr(x);
            s[0].erase({s[fx].rbegin()->fi+tag[fx],fx});
            tag[fx]+=y;
            s[0].insert({s[fx].rbegin()->fi+tag[fx],fx});
        }
        else if(ch[0]=='A'&&ch[1]=='3')
        {
            scanf("%d",&x);
            al+=x;
        }
        else if(ch[0]=='F'&&ch[1]=='1')
        {
            scanf("%d",&x);
            printf("%d\n",a[x]+tag[fr(x)]+al);
        }
        else if(ch[0]=='F'&&ch[1]=='2')
        {
            scanf("%d",&x);
            int fx=fr(x);
            multiset<pii>::iterator it=s[fx].end();it--;
            printf("%d\n",tag[fx]+(*it).fi+al);
        }
        else if(ch[0]=='F'&&ch[1]=='3')
        {
            printf("%d\n",s[0].rbegin()->fi+al);
        }
    }
}

P2825 HEOI2016/TJOI2016 游戏

考虑没有硬石头,每一行作为二分图的左部点,列作为右部点,如果可以在一个位置放炸弹,就把对应的列和行连一条边,做最大匹配即可。

对于一行中的硬石头,我们可以把这一行拆成两行来建图,列也做相同操作,行列有相交的部分就建边,做最大匹配即可。

#include<bits/stdc++.h>
using namespace std;

const int maxn=5005;

struct Edge
{
    int tot;
    int head[maxn];
    struct edgenode{int to,nxt;}edge[maxn*2];
    inline void add(int x,int y)
    {
        tot++;
        edge[tot].to=y;
        edge[tot].nxt=head[x];
        head[x]=tot;
    }
}G;

int n,m,cnt;
int idh[maxn][maxn],idw[maxn][maxn],cur[maxn];

char mp[maxn][maxn];

bool vis[maxn];

inline int dfs(int u)
{
    for(int i=G.head[u];i;i=G.edge[i].nxt)
    {
        int v=G.edge[i].to;
        if(vis[v]) continue;
        vis[v]=true;
        if(!cur[v]||dfs(cur[v]))
        {
            cur[v]=u;
            return 1;
        }
    }
    return 0;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>mp[i][j];
    for(int i=1;i<=n;i++)
    {
        cnt++;
        for(int j=1;j<=m;j++)
        {
            if(mp[i][j]=='#') continue;
            if(mp[i][j-1]=='#') cnt++;
            idh[i][j]=cnt;
        }
    }
    for(int i=1;i<=m;i++)
    {
        cnt++;
        for(int j=1;j<=n;j++)
        {
            if(mp[j][i]=='#') continue;
            if(mp[j-1][i]=='#') cnt++;
            idw[j][i]=cnt;
        }
    }
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(mp[i][j]=='*') G.add(idh[i][j],idw[i][j]);
    int ans=0;
    for(int i=1;i<=cnt;i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i)) ans++;
    }
    printf("%d",ans);
}

P4620 SDOI2018 荣誉称号

题解来自:洛谷 P4620 SDOI2018荣誉称号 | ctz's blog

发现需要处理的数据规模较大,但 \(k\) 较小,考虑缩小数据规模。

观察发现:

\[a_{x/2}+a_{x/2/2}+\cdots\equiv 0 \mod m \]

当 \(k=2\) 时会出现下面两个式子:

\[a_1+a_2+a_4\equiv 0 \mod m\\ a_2+a_4+a_8\equiv 0 \mod m \]

于是有 \(a_1\equiv a_8 \mod m\)。

发现二叉树上第 \(i\) 层与第 \(i+k+1\) 层节点同余。

只需要使得前 \(k+1\) 层余数为 \(0\),后面的层数自然满足余数为 \(0\),这样 dp 考虑的点数降为 \(2^{k+1}\) 个。

设 \(f[i][j]\) 表示 \(i\) 向下到达 \(k+1\) 层的和取模后为 \(j\) 的最小代价。(兄弟节点的取值一定是同余的,那么每一层的节点都是同余的)

可以预处理一个 \(v(i,j)\) 表示将点 \(i\) 及其同余的点改为 \(j\) 的代价。

\[f[i][j]=\min(f[\frac{i}{2}][k]+f[\frac{i}{2}+1][k]+v(i,j-k+m)) \]

从 \(2^{k+1}\) 开始倒序 dp,答案为 \(f[1][0]\),单次复杂度 \(O(n+m^22^{k+1})\)。

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const int maxn=1e7+5,maxm=3005;

int n,m,k;
int a[maxn],b[maxn],id[maxn];

ll v[maxm][205],f[maxm][205],tmpv[maxm][205];

unsigned int SA, SB, SC;int p, A, B;
unsigned int rng61(){
	SA ^= SA << 16;
	SA ^= SA >> 5;
	SA ^= SA << 1;
	unsigned int t = SA;
	SA = SB;
	SB = SC;
	SC ^= t ^ SA;
	return SC;
}
void gen(){
	scanf("%d%d%d%d%u%u%u%d%d", &n, &k, &m, &p, &SA, &SB, &SC, &A, &B);
	for(int i = 1; i <= p; i++)scanf("%d%d", &a[i], &b[i]);
	for(int i = p + 1; i <= n; i++){
		a[i] = rng61() % A + 1;
		b[i] = rng61() % B + 1;
	}
}

int main()
{
    int _;
    scanf("%d",&_);
    while(_--)
    {
        gen();k++;
        memset(v,0,sizeof(v));memset(tmpv,0,sizeof(tmpv));memset(f,0x3f,sizeof(f));
        for(int i=1;i<1<<k;i++) tmpv[id[i]=i][a[i]%=m]=b[i];
        for(int i=1<<k;i<=n;i++) tmpv[id[i]=id[i/(1<<k)]][a[i]%=m]+=b[i];
        for(int i=1;i<1<<k;i++)
        {
            for(int j=0;j<m;j++)
            {
                for(int k=0;k<j;k++) v[i][j]+=tmpv[i][k]*(j-k);
                for(int k=j+1;k<m;k++) v[i][j]+=tmpv[i][k]*(j+m-k);
            }
        }
        for(int i=1<<(k-1);i<1<<k;i++) for(int j=0;j<m;j++) f[i][j]=v[i][j];
        for(int i=(1<<(k-1))-1;i;i--)
        {
            for(int j=0;j<m;j++)
            {
                for(int k=0;k<m;k++)
                    f[i][j]=min(f[i][j],f[i<<1][k]+f[i<<1|1][k]+v[i][(j-k+m)%m]);
            }
        }
        printf("%lld\n",f[1][0]);
    }
}

标签:2024.10,05,int,fy,tot,fx,tag,maxn,刷题
From: https://www.cnblogs.com/binbin200811/p/18450675

相关文章

  • [ARC058F] 文字列大好きいろはちゃん
    题意给定\(n\)个字符串\(s_i\),你需要选择若干个字符串按从前往后的顺序拼起来使得总长为\(k\)且字典序最小,保证有解。\(n\le2000,k\le10^4,\sum|s_i|\le10^6\)分析先考虑一个显然的暴力DP:设\(f_{i,j}\)表示前\(i\)个串总长为\(j\)的最小字典序(注意这里由于限......
  • 2024.10.7 鲜花
    【UNR#3】百鸽笼花の塔君が持ってきた漫画くれた知らない名前のお花今日はまだ来ないかな?初めての感情知ってしまった窓に飾った絵画をなぞってひとりで宇宙を旅してそれだけでいいはずだったのに君の手を握ってしまったら孤独を知らないこの街にはもう二度と帰ってく......
  • 团队训练记录2024.10.7
    赛时依然和本校强队差两题比赛链接:https://codeforces.com/gym/104901A.ManyManyHeads这里先用栈处理好第一个状况,然后根据层数进行第二个状况是否存在判断#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;llgcd(llx,lly){if(y==0)retu......
  • 2024.10.7 test
    nf#33B有一棵包含\(n\)个节点的有根树,且树的高度不超过\(100\)。每次操作时可以选择一个节点\(u\),使其与父节点断开(如果有),成为一颗新树的根节点,然后删除以节点\(u\)为根的树中的所有叶节点。求删除所有节点所需的最少操作次数和通过最少次操作删除所有节点的方案数。\(n......
  • Day 24 贪心算法part02| LeetCode 122.买卖股票的最佳时机II,55.跳跃游戏,45.跳跃游戏II
    122.买卖股票的最佳时机II局部最优:每天的正利润全局最优:每天的正利润之和121.买卖股票的最佳时机classSolution{publicintmaxProfit(int[]prices){intres=0;for(inti=1;i<prices.length;i++){i......
  • Gym 100543G Virus synthesis 题解
    Solution首先只考虑回文串的答案;我们重点考虑的是偶回文串结论:对于偶回文串\(u\),从其最长的长度小于等于他的一半的回文后缀,或其父亲转移过来,一定是最优的证明:设\(u\)的一个回文子串为\(v\)(不是父亲),你要让\(v\tou\)的转移最优首先\(v\)不能跨过\(u\)的中点,因为此......
  • 2024.10.6训练记录
    下午cfA到!B签到题,考场还是写挂了,今天码力差。挂在while动指针的时候没有判右边界,似。唐诗程度不亚于数组开小。C1猜出来结论是第一次出现需要按照一开始的顺序就能过。C2把一开始的排列映射到[1,n]。修改时用set动态维护每个数第一次出现的位置。把第一次出现位置的......
  • 2024.10 做题记录 /
    CF2004E套用SG函数的结论,我们先打单个游戏的表再异或即可得到答案。首先对于一个大小为\(i\)的堆有\(SG[i]=\text{mex}_{j\boti}\{SG[i-j]\}\),容易暴力dp。intSG[N];intf(intx){ if(SG[x]!=-1)returnSG[x]; if(x==0)returnSG[0]=0; vector<int>g; up(i,1,x......
  • # 学期(如2024-2025-1) 学号20241405 《计算机基础与程序设计》第2周学习总结
    |这个作业属于哪个课程|2024-2025-1-计算机基础与程序设计)||这个作业要求在哪里|https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP/homework/13276))||这个作业的目标|数字化、信息安全、自学教材计算机科学概论(第七版)第1章并完成云班课测试、《C语言程序设计》第1章并......
  • 2024-2025-1 20241305 《计算机基础与程序设计》第二周学习总结
    作业信息这个作业属于哪个课程2024-2025-1-计算机基础与程序设计(https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP))这个作业要求在哪里2024-2025-1计算机基础与程序设计第二周作业(https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP/homework/13276))......