2019 ICPC EC-Final

2019 ICPC EC-Final重现


A.City

题意:

给n个m,表示有一个n*m的网格
在网格上任选两点连线,如果这条线的中点也在格点上则满足题意。
问有多少条这样的线(即端点在格点上,中点也在格点上,线的长度不为0)
数据范围:n,m<=1e3

思路:

枚举中点的位置即可。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    int n,m;
    cin>>n>>m;
    n++,m++;
    int ans=0;
    for(int i=1;i<=n;i++){//枚举中点
        for(int j=1;j<=m;j++){
            int a=min(i,n-i+1);
            int b=min(j,m-j+1);
            ans+=(a-1)*(b-1)*2+(a-1)+(b-1);
        }
    }
    cout<<ans<<endl;
    return 0;
}

C.Dirichlet k-th root(

卷积题,有空补


E.Flow

题意:

给定n个节点m条边的带权无向图。其中起点1到终点n有一些独立路径,且路径节点数相同(路径长度相同)。每条边有边权。
一次操作可以让一条边的边权加一,另外一条边权减一。问使得起点到终点的总流量最大最少需要操作多少次。
数据范围:n<=1e5,m<=2e5,边权<=1e9

思路:

因为路径长度是相同的。假设把所有边权都累加到一条链上,可以推出最大流=边权和/(链的长度),这就是最后的最大流。
将每条链按边权排序,然后分层,计算每一层的边权和。
因为要得到最大流,所以每一层的边权和都要大于等于最大流,小于最大流的就补全,这样就能计算出操作次数了。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
int head[maxm],nt[maxm<<2],to[maxm<<2],w[maxm<<2],tot;
vector<int>temp;
int flow[maxm];
int n,m;
void add(int x,int y,int z){
    tot++;nt[tot]=head[x];head[x]=tot;to[tot]=y;w[tot]=z;
}
void dfs(int x,int fa){
    if(x==n)return ;
    for(int i=head[x];i;i=nt[i]){
        int v=to[i];
        if(v==fa)continue;
        temp.push_back(w[i]);
        dfs(v,x);
    }
}
signed main(){
    cin>>n>>m;
    int sum=0;
    for(int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
        sum+=c;
    }
    int elen=0;//路径长度
    for(int i=head[1];i;i=nt[i]){
        temp.clear();
        temp.push_back(w[i]);
        dfs(to[i],1);
        sort(temp.begin(),temp.end());//排序
        if(!elen)elen=temp.size();
        for(int i=0;i<elen;i++){//分层求和
            flow[i+1]+=temp[i];
        }
    }
    int maxflow=sum/elen;
    int ans=0;
    for(int i=1;i<=elen;i++){
        if(flow[i]<maxflow){
            ans+=maxflow-flow[i];
        }
    }
    cout<<ans<<endl;
    return 0;
}

H.King

题意:

给一个长度为n的数组和一个质数p
定义一个序列s为king序列,序列s满足:s(i-1)*q%p=s(i),(模意义下的等比数列)
现在要你从a中调出一个子序列,这个子序列是king序列,问最大长度是多少,如果最大长度小于n/2则输出-1。
数据范围:n<=2e5,p<=1e9+7,保证p是质数

思路:

这题的关键点在于那个n/2,(果然题目的每一个条件都不能忽视!)
因为长度至少为n/2,所以这个子序列一定至少有两个数相邻或者相隔1,所以公比肯定会产生在相邻或者相隔1
用map存下所有a(i-1)到a(i)以及a(i-2)的到a(i)的公比数量
显然公比q的出现次数一定会大于等于某个数

将出现次数大于n/8的check一下取max即可(n/6也行,推了好久不知道这数咋来的)

code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxm=2e5+5;
unordered_map<int,int>mark;
int inv[maxm];
int a[maxm];
int n,p;
int ppow(int a,int b,int mod){
    int ans=1%mod;
    while(b){
        if(b&1)ans=(ll)ans*a%mod;
        a=(ll)a*a%mod;
        b>>=1;
    }
    return ans;
}
int cal(int q){
    unordered_map<int,int>dp;
    int ans=0;
    for(int i=n;i>=1;i--){
        dp[a[i]]=dp[(ll)a[i]*q%p]+1;
        ans=max(ans,dp[a[i]]);
    }
    return ans;
}
signed main(){
    int T;
    cin>>T;
    while(T--){
        unordered_map<int,int>t;
        mark=t;
        scanf("%d%d",&n,&p);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            inv[i]=ppow(a[i],p-2,p);
        }
        for(int i=2;i<=n;i++){
            int q=(ll)a[i]*inv[i-1]%p;
            mark[q]++;
        }
        for(int i=3;i<=n;i++){
            int q=(ll)a[i]*inv[i-2]%p;
            mark[q]++;
        }
        int ans=0;
        for(auto i:mark){
            if(i.second>=n/8){
                ans=max(ans,cal(i.first));
            }
        }
        if(ans*2<n)ans=-1;
        printf("%d\n",ans);
    }
    return 0;
}

M.Value

题意:

给长度为n的数组a和b
对于每一个位置i,可选可不选,如果选中了i,那么总得分就加上a(i)。
如果选中了i和j,且i,j>=2,且ik=j(k>=2),那么需要减去b(j),如果又多个i满足这个条件,b(j)会被减去多次。
现在你要找到一个选择方案,使得最后的总得分最大,输出这个最大得分。
数据范围:n<=1e5,1<=a(i),b(i)<=1e9

思路:

a(1)必选,因为他不影响其他数。
容易发现底数不同的数不会相互影响,例如8与9不会相互影响,因为8=23,9=32
发现n只有1e5,217大于1e5,因此如果按底数分组,最大的组是底数为2的组,不多于17个数,其他组数量更少。
所以可以先按底数分组,对于每一组然后二进制枚举选择方案取max,累加就是答案。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
vector<int>g[maxm];
int cnt=0;
int mark[maxm];
int a[maxm];
int b[maxm];
int solve(int x){//计算第x组的最大答案
    int len=g[x].size();
    int ans=0;
    for(int i=0;i<(1<<len);i++){
        for(int j=0;j<len;j++)mark[g[x][j]]=0;//清空标记
        int temp=0;
        for(int j=0;j<len;j++){
            if(i>>j&1){
                temp+=a[g[x][j]];
                temp-=mark[g[x][j]]*b[g[x][j]];
                for(int k=g[x][j]*g[x][j];k<=g[x][len-1];k*=g[x][j]){
                    mark[k]++;
                }
            }
        }
        ans=max(ans,temp);
    }
    return ans;
}
signed main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)cin>>b[i];
    for(int i=2;i<=n;i++){//按底数分组
        if(mark[i])continue;
        cnt++;
        for(int j=i;j<=n;j*=i){
            mark[j]=1;
            g[cnt].push_back(j);
        }
    }
    int ans=a[1];//a[1]必选,因为对后续无影响
    for(int i=1;i<=cnt;i++){//对每一组二进制枚举
        ans+=solve(i);
    }
    cout<<ans<<endl;
    return 0;
}

发布了430 篇原创文章 · 获赞 36 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/104741866