AGC009

吗了为什么我 T2 没开 long long。

吗了为什么我 T4 没想出来。

果然还是太菜。正在思考我现在在做什么,和意义在哪里。最近几天高强度 AGC 好像打的心态有点爆炸。明天如果不考试的话还有个洛谷月赛和 UER。而且现在看见右边一个黑色的难度标签就开始脑袋痛。

五题场,昨天和 artalter VP 了一下,切了前三题, T3 是 artalter 跟我说了一下大概的然后切的(然后不知道为什么她貌似只切了 T1 )。大概平均水平。毕竟才刚刚进入 2017 年。

[AGC009A] Multiple Array

普及题。

#define int long long
int n,a[100010],b[100010];
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i],&b[i]);
    int cnt=0;
    for(int i=n;i>=1;i--){
        int ret=ceil(1.0*(a[i]+cnt)/b[i])*b[i]-(a[i]+cnt);
        cnt+=ret;
    }
    printf("%lld\n",cnt);
    return 0;
}

[AGC009B] Tournament

一开始将近 20 分钟胡了一个从 \(1\) 开始往下搜,每个点要连的都从大到小排一遍序往下扫的做法,交上去发现挂了一半。然后发现这样的话有两条链长度相同可能会挂,从下往上扫就没问题了。于是就写了个树形 dp。其实也不叫 dp。

int n,dp[100010];
struct node{
    int v,next;
}edge[100010];
int t,head[100010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
bool cmp(int a,int b){
    return a>b;
}
void dfs(int x){
    vector<int>v;
    int tmp=0;
    for(int i=head[x];i;i=edge[i].next){
        dfs(edge[i].v);
        v.push_back(dp[edge[i].v]);
    }
    sort(v.begin(),v.end(),cmp);
    for(int a:v)tmp++,dp[x]=max(dp[x],a+tmp);
}
int main(){
    scanf("%d",&n);
    for(int i=2;i<=n;i++){
        int x;scanf("%d",&x);add(x,i);
    }
    dfs(1);
    printf("%d\n",dp[1]);
    return 0;
}

[AGC009C] Division into Two

一开始想了个 \(dp_{i,j,k}\) 为到第 \(i\) 个,\(X\) 集合上一个是 \(j\)\(Y\) 集合上一个是 \(K\) 的方案数,可以压掉一位,\(O(n^2)\) 还很难写。后来想了想可以只往一个集合里加数,然后还没细想 artalter 过来给我口胡了一个正解出来(

假设 \(A>B\)。设 \(dp_i\)\(X\) 集合选 \(i\) 的方案数。转移枚举之前放了哪个数,要求这两个数之间两两的差都 \(\ge B\) 。然后发现转移显然是一段区间,前缀和随便搞搞就行了。还挺好写的。

#define int long long
const int mod=1000000007;
int n,ans,dp[100010],sum[100010];
long long a,b,s[100010];
signed main(){
    scanf("%lld%lld%lld",&n,&a,&b);
    for(int i=1;i<=n;i++)scanf("%lld",&s[i]);
    if(a<b)swap(a,b);
    for(int i=2;i<n;i++){
        if(s[i+1]-s[i-1]<b){
            puts("0");return 0;
        }
    }
    dp[0]=sum[0]=1;int l=0,r=0;
    for(int i=1;i<=n;i++){
        while(r<i&&s[i]-s[r+1]>=a)r++;
        if(l<=r)dp[i]=(sum[r]-(l?sum[l-1]:0)+mod)%mod;
        sum[i]=(sum[i-1]+dp[i])%mod;
        if(i&&s[i]-s[i-1]<b)l=i-1;
    }
    for(int i=n;i>=0;i--){
        ans=(ans+dp[i])%mod;
        if(i<n&&s[i+1]-s[i]<b)break;
    }
    printf("%lld\n",ans);
    return 0;
}

[AGC009D] Uninity

人话翻译一下题意是找一棵树深度最小的点分树。

两篇洛谷题解都有点语焉不详。不多评价。

设每个节点的编号为在最终答案的 Uninity k 的树中它的 \(k\) 是多少。那么显然有一个引理就是对于两个编号相等的点,它们的路径上必然有一个编号大于它们的点。

考虑从下到上贪心地选取。记录一个集合 \(s_x\) 表示 \(x\) 的子树内哪些数不能再被选了。由于 \(k\)\(O(\log n)\) 的,可以压位。那么首先我们让 \(s_x\) 为所有儿子的 \(s\) 的或,那么能选取的点自然就是 \(s_x\) 中是 \(0\) 的位。然而还有一个问题,我们要保证子树中编号相等的点的路径上都有一个编号更大的点。这时我们取所有子树 \(s\) 的两两并集的交集,则 \(x\) 的编号必须比这个集合中最大的要大。然后就可以暴力扫一遍所有位确定 \(x\) 的编号。

确定了 \(x\) 的编号后还要决定 \(s_x\) 。首先 \(x\) 的编号已经被选了,那在保证有更大的之前就是不能选的。比编号大的显然不用动。比编号小的由于出现了比它们大的,所以就又都可以选了。

看不懂的看代码吧。

int n,ans,a[100010],dp[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 f){
    int s=0;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            dfs(edge[i].v,x);
            s|=dp[x]&dp[edge[i].v];
            dp[x]|=dp[edge[i].v];
        }
    }
    for(int i=31;i>=0;i--){
        if((s>>i)&1)break;
        if(!((dp[x]>>i)&1))a[x]=i;
    }
    dp[x]=(dp[x]>>a[x]|1)<<a[x];
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)ans=max(ans,a[i]);
    printf("%d\n",ans);
    return 0;
}

[AGC009E] Eternal Average

貌似某场 UR 也有这个套路。以后找个时间板刷 UR 去。

每次取平均数的过程可以看做一棵 \(k\) 叉树把 \(k\) 个儿子节点的平均数作为该节点的值。容易发现所有的数都可以表示为若干个 \(\dfrac 1{k^d}\)\(d\) 是深度。

如果枚举每层有多少 \(1\) 会算重。实际上既然我们只关心最后的值,不妨强制每层的 \(1\) 的数量不超过 \(k-1\) 。暴力背包即可。

const int mod=1000000007;
int n,m,K,ans,dp[4010][2010];
int main(){
    scanf("%d%d%d",&n,&m,&K);
    int cnt=(n+m-1)/(K-1);
    dp[0][0]=1;
    for(int i=1;i<=cnt;i++){
        for(int j=0;j<K;j++){
            for(int k=j;k<=min(m,i*K);k++){
                dp[i][k]=(dp[i][k]+dp[i-1][k-j])%mod;
            }
        }
    }
    for(int i=m;i>=1;i-=K-1){
        ans=(ans+dp[cnt][i])%mod;
        cnt--;
    }
    printf("%d\n",ans);
    return 0;
}

完了。

posted @ 2022-11-18 20:50  gtm1514  阅读(39)  评论(0编辑  收藏  举报