首页 > 其他分享 >AGC013

AGC013

时间:2023-05-28 17:47:22浏览次数:36  
标签:node AGC013 int ++ 100010 ans include

开始重新板刷 AGC。别惦记着你那 b 多项式了!然后发现我做题量太少了。

现在思维强度不太上档次,T1 都能挂一个星期。

都干嘛呢?看了一圈,洛谷没人提交(除了 H_Kaguya 写了个左偏树),vjudge 也没人交题,真都写 APIO 呢?那咋 T1 没人交?

[AGC013A] Sorted Arrays

普及题。这种东西是真的容易写挂。这题红确实有点低。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>
#include <iostream>
using namespace std;
int n,ans,a[100010];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int od=0;
    for(int i=2;i<=n;i++){
        if(a[i]==a[i-1])continue;
        if(!od){
            if(a[i]>a[i-1])od=1;
            else od=-1;
        }
        else if(od==1){
            if(a[i]>a[i-1])continue;
            else ans++,od=0;
        }
        else{
            if(a[i]<a[i-1])continue;
            else ans++,od=0;
        }
    }
    printf("%d\n",ans+1);
    return 0;
}

[AGC013B] Hamiltonish Path

简单题,随便找一个点用力左右扩展即可。要 dfs 两次。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;
int n,m,p[100010],q[100010];
bool v[100010];
struct node{
    int v,next;
}edge[200010];
int t,head[100010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
void dfs(int x,int p[]){
    v[x]=true;p[++p[0]]=x;
    for(int i=head[x];i;i=edge[i].next){
        if(!v[edge[i].v]){
            dfs(edge[i].v,p);return;
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,p);dfs(1,q);
    printf("%d\n",p[0]+q[0]-1);
    for(int i=q[0];i>1;i--)printf("%d ",q[i]);
    for(int i=1;i<=p[0];i++)printf("%d ",p[i]);puts("");
    return 0;
}

[AGC013C] Ants on a Circle

首先两个蚂蚁碰到了改变方向这个可以看成两个蚂蚁互相穿过。这样位置是容易算的,不需要考虑相遇。只要找到第一只蚂蚁是哪个就好了。

假如有一只蚂蚁顺时针穿过了 \(0\) 位置,那么 \(1\) 号的位置就会往后一个,反之往前一个。这样只要算所有蚂蚁穿过 \(0\) 多少次就行了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int n,l,t,st,ans[100010];
int main(){
    scanf("%d%d%d",&n,&l,&t);
    for(int i=0;i<n;i++){
        int x,d;scanf("%d%d",&x,&d);
        if(d==1)x+=t;
        else x-=t;
        st=(st+(int)floor(1.0*x/l)%n+n)%n;
        ans[i]=(x%l+l)%l;
    }
    sort(ans,ans+n);
    for(int i=0;i<n;i++)printf("%d\n",ans[(st+i)%n]);
    return 0;
}

[AGC013D] Piling Up

比较逆天的。

首先显然的格路计数模型。然后每次操作可以看成增加 / 不变 / 减少一个黑球。这样设 \(dp_{i,j}\) 为 \(i\) 次操作剩下 \(j\) 个黑球的方案数就能简单 dp。

但是这样显然会重。一个神奇的 trick 是把所有相同形态折线中最低的一个看做答案,而最低的一个一定经过 \(0\),于是只计算经过 \(0\) 的折线个数就不会重了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int mod=1000000007;
int n,m,dp[3010][3010][2];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)dp[0][i][0]=1;
    dp[0][0][1]=1;
    for(int i=1;i<=m;i++){
        for(int j=0;j<=n;j++){
            if(j){
                dp[i][j][0]=(dp[i][j][0]+dp[i-1][j-1][0])%mod;
                dp[i][j][1]=(1ll*dp[i][j][1]+dp[i-1][j-1][1]+dp[i-1][j][1])%mod;
                if(j==1)dp[i][j][1]=(dp[i][j][1]+dp[i-1][j][0])%mod;
                else dp[i][j][0]=(dp[i][j][0]+dp[i-1][j][0])%mod;
            }
            if(j<n){
                dp[i][j][1]=(1ll*dp[i][j][1]+dp[i-1][j+1][1]+dp[i-1][j][1])%mod;
                if(!j)dp[i][j][1]=(dp[i][j][1]+dp[i-1][j+1][0])%mod;
                else dp[i][j][0]=(1ll*dp[i][j][0]+dp[i-1][j+1][0]+dp[i-1][j][0])%mod;
            }
        }
    }
    int ans=0;
    for(int i=0;i<=n;i++)ans=(ans+dp[m][i][1])%mod;
    printf("%d\n",ans);
    return 0;
}

[AGC013E] Placing Squares

大诈骗题。一开始一直看着这像个数学题,然后不会。然后看题解给我摆了一个矩阵上来,乐。

首先这个平方不好刻画,给它个组合意义。可以变成在一段里边放两个球,位置可以重。那 \(dp_{i,0/1/2}\) 为到 \(i\),当前段放了 \(j\) 个球,转移显然。然后写个 \(3\times 3\) 矩阵递推这个 dp。标记点的转移式子把分段的情况去掉即可。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int mod=1000000007;
int n,m,pos=-1;
struct node{
    int a[4][4];
    node(){memset(a,0,sizeof(a));}
    node operator*(node s){
        node ret;
        for(int i=1;i<=3;i++)for(int j=1;j<=3;j++)for(int k=1;k<=3;k++){
            ret.a[i][j]=(ret.a[i][j]+1ll*a[i][k]*s.a[k][j])%mod;
        }
        return ret;
    }
}a,b,ans;
node qpow(node a,int b){
    node ans;for(int i=1;i<=3;i++)ans.a[i][i]=1;
    while(b){
        if(b&1)ans=ans*a;
        a=a*a;
        b>>=1;
    }
    return ans;
}
void solve(int x){
    node ret=b*qpow(a,x-pos-1);
    ans=ret*ans;pos=x;
}
int main(){
    scanf("%d%d",&n,&m);
    a.a[1][1]=a.a[1][3]=a.a[2][2]=a.a[3][1]=a.a[3][2]=1;
    a.a[2][1]=a.a[2][3]=a.a[3][3]=2;
    b.a[1][1]=b.a[2][2]=b.a[3][1]=b.a[3][2]=b.a[3][3]=1;
    b.a[2][1]=2;
    ans.a[1][1]=1;
    for(int i=1;i<=m;i++){
        int x;scanf("%d",&x);
        node ret=b*qpow(a,x-pos-1);
        ans=ret*ans;pos=x;
    }
    ans=qpow(a,n-pos-1)*ans;
    printf("%d\n",ans.a[3][1]);
    return 0;
}

[AGC013F] Two Faced Cards

我不会的题。是不是 AT 的题但凡上 3600 我就不会。

首先显然二分图匹配模型。用 Hall 定理转化问题,那么 \(X\) 的值可以变成给某个初始全 \(0\) 的序列 \(A\) 后缀 \(+1\),\(Y\) 就是后缀 \(-1\),最终如果 \(A\) 所有位置非负则合法。然后钦定每个位置先选 \(a\),变成给定序列 \(A\) 和若干区间 \(+1\),选尽量少的区间使得 \(A\) 非负。如果加上询问,枚举询问选哪一个,就变成需要 \(A\) 的一个前缀 \(\ge 0\),剩下的 \(\ge -1\)。

那么接下来考虑哪些区间必须选。有一个自己做想不出来的结论:对于最大的 \(A_i<-1\) 的位置,选左端点最小的包含 \(i\) 的区间一定不劣。那么开个堆贪心即可。

剩下的就是全是 \(\ge -1\) 的了,那么对于每个前缀处理出使这个前缀都 \(\ge 0\) 最少选几个区间,同样可以贪心选右端点最靠右的,过程和上边大体相同。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
int n,m,a[100010],b[100010],c[100010],cnt[100010],s[100010],ans[100010];
bool vis[100010];
struct node{
    int l,x;
    bool operator<(const node&s)const{
        return l<s.l;
    }
    bool operator>(const node&s)const{
        return l>s.l;
    }
};
priority_queue<node,vector<node>,greater<node> >q;
priority_queue<node>p;
vector<int>v[100010];
int main(){
    scanf("%d",&n);n++;
    for(int i=1;i<n;i++)scanf("%d%d",&a[i],&b[i]);
    for(int i=1;i<=n;i++)scanf("%d",&c[i]);
    sort(c+1,c+n+1);
    for(int i=1;i<n;i++){
        a[i]=lower_bound(c+1,c+n+1,a[i])-c;
        b[i]=lower_bound(c+1,c+n+1,b[i])-c;
        cnt[a[i]]++;
    }
    for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1]-1;
    for(int i=1;i<n;i++){
        if(a[i]<=b[i])vis[i]=true;
        else v[a[i]-1].push_back(i);
    }
    scanf("%d",&m);
    int sum=0;
    for(int i=n;i>=1;i--){
        for(int x:v[i])q.push({b[x],x});
        sum+=s[i];
        while(cnt[i]+sum<-1){
            while(!q.empty()&&q.top().l>i)q.pop();
            if(q.empty()){
                while(m--)puts("-1");
                return 0;
            }
            vis[q.top().x]=true;
            int l=b[q.top().x],r=a[q.top().x]-1;q.pop();
            sum++;s[r]++;s[l-1]--;
            ans[1]++;
        }
    }
    sum=s[n+1];
    for(int i=n;i>=1;i--){
        sum+=s[i];s[i]=0;v[i].clear();
        cnt[i]+=sum;
    }
    for(int i=1;i<n;i++){
        if(!vis[i])v[b[i]].push_back(i);
    }
    sum=0;
    for(int i=1;i<=n;i++){
        ans[i+1]=ans[i];
        for(int x:v[i])p.push({a[x]-1,x});
        sum+=s[i];
        bool jud=false;
        while(cnt[i]+sum==-1){
            while(!p.empty()&&p.top().l<i)p.pop();
            if(p.empty()){
                for(int j=i;j<=n;j++)ans[j+1]=n+1;
                jud=true;break;
            }
            int l=b[p.top().x],r=a[p.top().x]-1;p.pop();
            sum++;s[l]++;s[r+1]--;
            ans[i+1]++;
        }
        if(jud)break;
    }
    while(m--){
        int d,e;scanf("%d%d",&d,&e);
        d=lower_bound(c+1,c+n+1,d)-c;
        e=lower_bound(c+1,c+n+1,e)-c;
        printf("%d\n",max(n-min(ans[d],ans[e]+1),-1));
    }
    return 0;
}

标签:node,AGC013,int,++,100010,ans,include
From: https://www.cnblogs.com/gtm1514/p/17438533.html

相关文章

  • [AGC013C] Ants on a Circle 解题报告
    洛谷题面AT题面CF625F先考虑弱化版,若是不考虑编号怎么办。这个问题有一个很经典的结论,碰撞等同穿过,所以直接算出每个点按照指定方向走,在\(t\)秒后的位置即可。现在多了一个编号,因为是碰撞,所以两个点的相对位置是相同的,即\(x\)号点原来是\(y\)号点顺时针方向的第几个点,......
  • 「解题报告」AGC013E Placing Squares
    想了一会然后看题解,翻到日文题解然后又关了,然后突然会了,怎么回事第一眼生成函数!做不了。考虑经典拆贡献方法,把平方的贡献变成从区间中选两个数的方案数。这样我们可以用一个DP来计数。设\(f_{i,j}\)表示到了第\(i\)格,已经选了\(j\)个数的方案数。如果没有限制,那么就直......
  • Atcoder题解:Agc013_e
    我们考虑转化题意,一个合法的将\(1\simN\)划分成长度依次为\(a_1,a_2,\cdotsa_k\)的小区间,对答案的贡献为\(a_1^2a_2^2\cdotsa_k^2\)。化贡献为方案数,我们在每个长度为\(a_i\)的小区间内放置两个独立的标记,每个合法的划分方案对放置标记方案种数的贡献恰好是其对最终答......
  • [AGC013B] Hamiltonish Path
    个人思路:随便从一个节点开始搜索,只要当前节点不满足条件,随便找一个与它有边相连,不在序列里的节点加入序列。因为要么中途停止,要么把所有节点遍历一遍,一定能找到一个端点。......
  • AGC013E
    模型转化题,转化不出来就白给。可以把题目的条件翻译成以下组合语言:有一排\(n\)个格子,你要在其中插入若干个隔板将其隔成若干段有\(m\)个特殊格子\(a_1,a_2,\dots,......
  • 【AGC013D】Piling Up(神奇的dp)
    考场上用了一种奇怪的做法,不知道为什么就对了,考完后仔细想才想明白。很巧妙的一种dp方式。首先发现每次操作是拿一个球、放两个球、再拿一个球,总球数不变,所以有\(\tex......
  • AT2371 [AGC013E] Placing Squares
    AT2371[AGC013E]PlacingSquares设\(f_i\)表示考虑到第\(i\)个点的贡献之和且不考虑坏点。不难发现转移方程为\(f_n=\sum_{i=0}^nf_i\times(n-i)^2\)则当第\(n......
  • 【题解】「AGC013D」Piling Up
    传送门题目大意:开始有\(n\)个黑白球在袋子中,但是不知道具体多少黑球白球,有\(m\)次操作,每次从袋子中先拿一个球,再放入一个黑球一个白球,再拿走一个球,求所有初始情况下......
  • AGC013 记录
    赛次总结A.SortedArrays题意给定一个长度为\(n\)的数组,可以划分为多少个单调不减或单调不增的数组。【AC】B.HamiltonishPath题意给定一个\(n\)个点\(m\)......