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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章