CF2275(Div.3)做题记录

CF2275

A. In Search of Convenience

*800

数据很小,暴力枚举。

Code:

#include<bits/stdc++.h>
using namespace std;
int T;
int main(){
    scanf("%d",&T);
    while(T--){
        int xx,yy;
        scanf("%d%d",&xx,&yy);
        int R;
        scanf("%d",&R);
        int ansi=0,ansj=0;
        for(int i=0;i<=25;i++){
            for(int j=0;j<=25;j++){
                if(i*i+j*j==R*R){
                    ansi=i,ansj=j;
                }
            }
        }
        printf("%d %d\n",xx+ansi,yy+ansj);
    }
    return 0;
}

B. Did Not Go to Print

*800

模拟它的操作。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int T;
int n;
int s[N],top;
bool vis[N];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        top=0;
        memset(vis,0,(n+1)*sizeof(bool));
        int tot=n;
        for(int i=1;i<=n;i++){
            int x;
            scanf("%1d",&x);
            if(x==1)s[++top]=i;
            else if(x==2){
                if(top){
                    vis[s[top]]=true;
                    top--;
                }
                else{
                    vis[i]=true;
                }
                tot--;
            }
            else{
                vis[i]=true;
                tot--;
            }
        }
        printf("%d\n",tot);
        if(!tot)puts("");
        else{
            for(int i=1;i<=n;i++){
                if(!vis[i])printf("%d ",i);
            }
            puts("");
        }
    }
    return 0;
}

C. Unrequited Love

*1000

开桶,统计 \(a_x+a_{x+2}-a_{x+4}\) 值的数量。特判 \(x=y-4\),\(x=y-2\),\(x=y\),\(x=y+2\),\(x=y+4\),不能将它们统计在内。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int T;
int n;
int a[N],val[N];
int arr[N];
int *tong=arr+40005;
int s[N],top;
void init(){
    for(int i=1;i<=top;i++){
        tong[s[i]]=0;
    }
    top=0;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        init();
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n-4;i++){
            val[i]=a[i]+a[i+2]-a[i+4];
            tong[val[i]]++;
            s[++top]=val[i];
        }
        ll ans=0;
        for(int i=1;i<=n-4;i++){
            ans+=tong[val[i]];
            if(i-4>=1&&val[i-4]==val[i])ans--;
            if(i-2>=1&&val[i-2]==val[i])ans--;
            ans--;
            if(i+2<=n-4&&val[i+2]==val[i])ans--;
            if(i+4<=n-4&&val[i+4]==val[i])ans--;
        }
        printf("%lld\n",ans>>1);
    }
    return 0;
}

D. Precision Alignment

*1300

我们发现,只要存在 \(a_i>b_i\) 或 \(b_i>c_i\) ,就可以对它进行不停操作,\(a_i+b_i+c_i\) 就可以一直增加。不存在的有2种可能。1. \(a_i=b_i=c_i\) 。此时操作是无效的,增加不了。2. \(a_i \le b_i\) 且 \(b_i \le c_i\) ,此时操作只会让总和减小。但不断操作某一个,例如操作 \(a_i,b_i\) ,会让 \(c_i\) 不断减小,直到 \(b_i>c_i\) 时,转化为一直增加的情况。这样的代价最小是 \(2 \times \min(b_i-a_i+1,c_i-b_i+1)\) 。我们要想让所有值中最小的最大,可以二分答案,计算每个 \(i\) 还差多少次操作可以达到要求。时间复杂度 \(O(n \log k)\) 。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
const ll inf=0x3f3f3f3f3f3f3f3f;
int T;
int n;
ll k;
ll a[N],b[N],c[N];
bool check(ll mid){
    ll tot=0;
    for(int i=1;i<=n;i++){
        ll val=a[i]+b[i]+c[i];
        if(val>=mid)continue;
        tot+=mid-val;
        if(a[i]==b[i]&&b[i]==c[i])return false;
        if(c[i]>=b[i]&&b[i]>=a[i]){
            tot+=min(c[i]-b[i]+1,b[i]-a[i]+1)<<1;
        }
        if(tot>k)return false;
    }
    if(tot>k)return false;
    else return true;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%lld",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        }
        ll l=-inf,r=inf;
        while(l<r){
            ll mid=(l+r+1)>>1;
            if(check(mid)==true)l=mid;
            else r=mid-1;
        }
        printf("%lld\n",l);
    }
    return 0;
}

E. Repentance Is Already on the Way

*1500

假设有基本的连接方式:\(a_1 \rightarrow b_1 \rightarrow a_2 \rightarrow b_2 \rightarrow ... \rightarrow a_n \rightarrow b_n\) 。我们发现,这样连接(从 \(a_1\) 开始,中间没有间隔点)总是 \(b_i \rightarrow a_{i+1}\) 。还发现,\(b_i\) 不会有 \(b_i \rightarrow a_i,a_{i-1}\) (因为我们是从 \(a_1\) 开始的,如果出现这个情况,一定会出现有 \(b_i\) 无法被连接)。那么,如果有间隔点呢?假设从某个点开始,\(a_i \rightarrow b_{i+1}\) ,那么 \(b_i\) 只能由右侧 \(a_{i+1} \rightarrow b_i\) ,形成交错的情况。这样讨论下来,就只有不间隔和交错连接两种情况。讨论两种情况的分界 \(i\) ,取最大值。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int T;
int n;
int a[N],b[N];
int fval(int x,int y){
    if(x==y)return 2;
    else return 1;
}
int s0[N];  //左边以i结尾
int s1[N][2];  //交错以i开始/结尾
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)scanf("%d",&b[i]);
        s0[1]=0;
        for(int i=2;i<=n;i++){
            s0[i]=s0[i-1]+fval(a[i],b[i-1])+fval(a[i-1],b[i-1]);
        }
        s1[n][0]=s1[n][1]=0;
        for(int i=n-1;i>=1;i--){
            s1[i][0]=s1[i+1][1]+fval(a[i],b[i+1]);
            s1[i][1]=s1[i+1][0]+fval(b[i],a[i+1]);
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            ans=max(ans,s0[i]+s1[i][0]+s1[i][1]+fval(a[n],b[n]));
        }
        printf("%d\n",ans);
    }
    return 0;
}

F. Tea Blend

*1800

要求因子数量(也就是 \(d(n)\) )为奇数,这让我们想到了:如果 \(\displaystyle n=\sum_{i=1}^{t}{p_i^{k_i}}\) ,那么 \(\displaystyle d(n)=\sum_{i=1}^{t}{(k_i+1)}\) 。让 \(d(n)\) 为奇数,那 \(k_i+1\) 就不能为偶数。我们的目标就变成了:计算 \(F(i,j)\) 中所有质因子的指数,统计所有指数都不为奇数的数量。\((i,j)\) 太多了,不能枚举,但我们想到,如果 \(a_i\) 和 \(\displaystyle \prod_{k=1}^{j}{a_k}\) 在相同质因子的指数奇偶性相同,那乘起来一定都是偶数。所以,我们要统计 \(a_i\) 和 \(\displaystyle \prod_{k=1}^{j}{a_k}\) 的奇偶性情况。\(a_i \le 10^6\) ,那一共有 \(78498\) 个质数,太多了,我们想到用哈希存下情况,方便我们比较。当然,所有质因子的数量很多。我们要提前算出 \(1 \sim \max{a_i}=M\) 的质因子,时间复杂度是 \(O(M \log M)\) 的。整个程序时间复杂度大约是 \(O(n \log{n})\) 的。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod1=1e9+7,mod2=1e9+9;
const ll pm=131;
const int N=2e5+5;
const int A=1e6;
int cnt;
int p[N];
bool visp[A+5];
void prime(){
    visp[1]=true;
    for(int i=2;i<=A;i++){
        if(!visp[i])p[++cnt]=i;
        for(int j=1;j<=cnt&&1LL*i*p[j]<=A;j++){
            visp[i*p[j]]=true;
            if(i%p[j]==0)break;
        }
    }
}
int T;
int n;
int a[N];
int arr[A+5];
vector<int>t[A+5];
bool vis[N];
struct node{
    ll val1,val2;
    bool operator<(const node& x)const{
        if(val1==x.val1)return val2<x.val2;
        else return val1<x.val1;
    }
};
node pw[N];
map<node,int>mp;
void init(){
    for(int i=1;i<=n;i++){
        for(auto j:t[a[i]]){
            vis[j]=false;
        }
    }
    mp.clear();
}
int main(){
    prime();
    for(int i=1;i<=A;i++)arr[i]=i;
    for(int i=1;i<=cnt;i++){
        for(int j=p[i];j<=A;j+=p[i]){
            int tot=0;
            while(arr[j]%p[i]==0){
                tot++;
                arr[j]/=p[i];
            }
            if((tot&1)==1){
                t[j].emplace_back(i);
            }
        }
    }
    pw[0].val1=pw[0].val2=1;
    for(int i=1;i<=cnt;i++){
        pw[i].val1=(pw[i-1].val1*pm)%mod1;
        pw[i].val2=(pw[i-1].val2*pm)%mod2;
    }
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        ll now1=0,now2=0;
        for(int i=1;i<=n;i++){
            for(auto j:t[a[i]]){
                if(vis[j]==false){
                    vis[j]=true;
                    now1=(now1+pw[j-1].val1)%mod1;
                    now2=(now2+pw[j-1].val2)%mod2;
                }
                else{
                    vis[j]=false;
                    now1=(now1+mod1-pw[j-1].val1)%mod1;
                    now2=(now2+mod2-pw[j-1].val2)%mod2;
                }
            }
            if(!mp.count({now1,now2}))mp[{now1,now2}]=1;
            else mp[{now1,now2}]++;
        }
        ll ans=0;
        for(int i=1;i<=n;i++){
            ll now1=0,now2=0;
            for(auto j:t[a[i]]){
                now1=(now1+pw[j-1].val1)%mod1;
                now2=(now2+pw[j-1].val2)%mod2;
            }
            if(mp.count({now1,now2})){
                ans+=mp[{now1,now2}];
            }
        }
        printf("%lld\n",ans);
        init();
    }
    return 0;
}

G. Copper Squander

*1900

如果我们不买线条,那答案就是最小生成树。如果买呢?可以确定,留下的也一定是最小生成树中较小的那几条边(回想一下 Kruskal 的步骤。想象有几条边我们没选,这时树会变成几个连通块。如果我们买,直接花费这个代价;如果不买,就要选出一条边,最小并连接起两个连通块,这条边肯定是最小生成树上的边)。那怎么找到要买几条边?假设我们从买 \(n-1\) 条边开始(也就是保留 \(0\) 条边),我们会有 \(\displaystyle \sum{d_i} - \frac{n(n-1)}{2}x_j\) 的收益。在此基础上,保留一条边,收益 \(-e_1+(n-1)x_j\) (假设 \(e_i\) 就是第 \(i\) 条最小生成树边的代价),保留第 \(i\) 条就会在之前的基础上得到 \(-e_i+(n-i)x_j\) 的收益。我们发现,\(-e_i\) 和 \((n-i)x_j\) 都是单调递减的。所以,想让这些收益的和最大,我们就二分找到收益由正转负的分界点,统计收益。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int T;
int n,m,q;
struct edge{
    int x,y,val;
    bool operator<(const edge z)const{
        return val<z.val;
    }
}e[N];
int fx[N];
int getf(int x){
    if(fx[x]==x)return x;
    int fn=getf(fx[x]);
    return fx[x]=fn;
}
ll d[N],s[N];
bool check(int mid,ll x){
    if(mid==0)return true;
    if(-d[mid]+1LL*(n-mid)*x>=0)return true;
    else return false;
}
void init(){
    for(int i=1;i<=n;i++)fx[i]=i;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&q);
        init();
        ll sum=0;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].val);
            sum+=e[i].val;
        }
        sort(e+1,e+1+m);
        int cnt=0;
        for(int i=1;i<=m;i++){
            int x=e[i].x,y=e[i].y;
            int ux=getf(x),uy=getf(y);
            if(ux==uy)continue;
            fx[ux]=uy;
            d[++cnt]=e[i].val;
        }
        for(int i=1;i<=cnt;i++){
            s[i]=s[i-1]+d[i];
        }
        while(q--){
            ll x;
            scanf("%lld",&x);
            ll ans=sum-1LL*n*(n-1)/2*x;
            int l=0,r=cnt;
            while(l<r){
                int mid=(l+r+1)>>1;
                if(check(mid,x)==true)l=mid;
                else r=mid-1;
            }
            ans=ans-s[l]+1LL*((n-1)+(n-l))*l/2*x;
            printf("%lld ",ans);
        }
        puts("");
    }
    return 0;
}

H. A Problem to Warm Up the Eyebrows

*2400

每个 \(S^2=(a_1+a_2+...+a_{n*m})(a_1+a_2+...+a_{n*m})\) 。把它展开,就是许多个 \(a_i,a_j\) 相乘。不妨交换求和顺序,我们讨论选择两个点对整个答案的贡献。我们找到能框住两个点的矩形。假设两个点 \((x_1,y_1) \ ,(x_2,y_2) \ , x_1 \le x_2 \ , y_1 \le y_2\),贡献为 \(2\displaystyle \sum_{i=1}^{x_1}\sum_{j=x_2}^{n}\sum_{k=1}^{y_1}\sum_{l=y_2}^{m}{(-1)^{j-i+1+l-k+1}a_{x_1,y_1}a_{x_2,y_2}}\) 。\((-1)^{\alpha}\) 这种很有特点,上式可以转化为 \(\displaystyle 2a_{x_1,y_1}a_{x_2,y_2} \sum_{i=1}^{x_1}{(-1)^{i}} \sum_{i=x_2}^{n}{(-1)^{i}} \sum_{i=1}^{y_1}{(-1)^{i}} \sum_{i=y_2}^{m}{(-1)^{i}}\) 。这样,系数就只跟 \(x_1,x_2,y_1,y_2\) 自己有关了。我们分为3类统计:1.点在当前点左上方,左方或上方。我们利用分配律,前缀和统计这些点的贡献,在乘上自己这个点的系数。2.这样其实不能统计到所有点,还要统计点在当前点右上方。开一个从右上向左下的前缀和。3.自己相乘。此时,根据 \(S^2\) 的展开式,贡献中是没有 \(2\) 这个系数的。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e6+5;
const ll mod=1e9+7;
int T;
int n,m;
int fid(int x,int y){
    return x*(m+2)+y;
}
ll a[N];
ll s1[N],s2[N];
ll f(int l,int r){
    int len=r-l+1;
    if((len&1)==0)return 0;
    else{
        if((l&1)==0)return 1;
        else return -1;
    }
}
void init(){
    for(int i=0;i<=n;i++){
        for(int j=0;j<=m+1;j++){
            s1[fid(i,j)]=s2[fid(i,j)]=0;
        }
    }
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%lld",&a[fid(i,j)]);
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                s1[fid(i,j)]=s1[fid(i-1,j)]+s1[fid(i,j-1)]-s1[fid(i-1,j-1)]
                +f(1,i)*f(1,j)*a[fid(i,j)];
                s1[fid(i,j)]=(s1[fid(i,j)]%mod+mod)%mod;
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=m;j>=1;j--){
                s2[fid(i,j)]=s2[fid(i-1,j)]+s2[fid(i,j+1)]-s2[fid(i-1,j+1)]
                +f(1,i)*f(j,m)*a[fid(i,j)];
                s2[fid(i,j)]=(s2[fid(i,j)]%mod+mod)%mod;
            }
        }
        ll ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                ans+=2LL*a[fid(i,j)]*f(i,n)*f(j,m)*((s1[fid(i-1,j)]+s1[fid(i,j-1)]-s1[fid(i-1,j-1)]+mod)%mod);
                ans=(ans%mod+mod)%mod;
                ans+=2LL*a[fid(i,j)]*f(i,n)*f(1,j)*s2[fid(i-1,j+1)];
                ans=(ans%mod+mod)%mod;
                ans+=a[fid(i,j)]*a[fid(i,j)]*f(1,i)*f(i,n)*f(1,j)*f(j,m);
                ans=(ans%mod+mod)%mod;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2026-10-30 11:24  TanHaoren  阅读(2)  评论(0)    收藏  举报