2019|2019 ICPC EC-Final

2019 ICPC EC-Final重现
A.City 题意: 给n个m,表示有一个n*m的网格
在网格上任选两点连线,如果这条线的中点也在格点上则满足题意。
问有多少条这样的线(即端点在格点上,中点也在格点上,线的长度不为0)
数据范围:n,m<=1e3
思路: 枚举中点的位置即可。
code:

#include 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<

C.Dirichlet k-th root( 卷积题,有空补
E.Flow 题意: 【2019|2019 ICPC EC-Final】给定n个节点m条边的带权无向图。其中起点1到终点n有一些独立路径,且路径节点数相同(路径长度相同)。每条边有边权。
一次操作可以让一条边的边权加一,另外一条边权减一。问使得起点到终点的总流量最大最少需要操作多少次。
数据范围:n<=1e5,m<=2e5,边权<=1e9
思路: 因为路径长度是相同的。假设把所有边权都累加到一条链上,可以推出最大流=边权和/(链的长度),这就是最后的最大流。
将每条链按边权排序,然后分层,计算每一层的边权和。
因为要得到最大流,所以每一层的边权和都要大于等于最大流,小于最大流的就补全,这样就能计算出操作次数了。
code:
#include 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; vectortemp; 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

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 using namespace std; #define ll long long const int maxm=2e5+5; unordered_mapmark; 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_mapdp; 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_mapt; 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

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 using namespace std; #define int long long const int maxm=1e5+5; vectorg[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<>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<

    推荐阅读