【寒假集训系列2.14】
摆脱了110魔咒...
30+100+0=130
诶呀第三题没开long long一分都没有啊...
T1Gcd
题目描述:
给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对.
输入:
一个整数N
输出:
如题
样例输入:
4
样例输出:
4
数据规模:
30%:N<=5000
100%: N<=10^7
这道题似乎以前听过啊...然而不太会诶,枚举gcd,然后找互质的数,突然一下子就忘记欧拉函数这个东西了...然后暴力于是30分
正解:欧拉筛一下,求出欧拉函数,把所有互质的个数求出来,然后枚举gcd(),即质数,当然那些质数对(x,x)(x是质数)会被算两次,要减掉
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<cstdlib> 6 #include<ctime> 7 #include<cmath> 8 inline int read(){ 9 int ans=0,f=1;char chr=getchar(); 10 while(!isdigit(chr)){if(chr=='-')f=-1;chr=getchar();} 11 while(isdigit(chr)) {ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();} 12 return ans*f; 13 }const int N=1e7+5; 14 int n,prime[N],p[N],cnt,phi[N]; 15 long long sum[N],ans; 16 void pre(){ 17 phi[1]=1; 18 for(int i=2;i<=n;++i){ 19 if(prime[i]==0) 20 p[++cnt]=i,phi[i]=i-1; 21 for(int j=1;j<=cnt;++j){ 22 if(i*p[j]>n) break; 23 prime[i*p[j]]=1; 24 phi[i*p[j]]=(i%p[j]==0)?p[j]*phi[i]:phi[i]*(p[j]-1);//欧拉函数(积性函数) 25 } 26 } 27 } 28 int main(){ 29 // freopen("gcd.in","r",stdin); 30 // freopen("gcd.out","w",stdout); 31 n=read();pre(); 32 for(int i=1;i<=n;++i) sum[i]=sum[i-1]+phi[i];//前缀和 33 for(int i=1;i<=cnt;++i) ans+=sum[n/p[i]]*2; 34 printf("%lld\n",ans-cnt);//减掉质数对个数 35 }
T2宝藏
题目描述:
老胡在出模拟赛时想到了这样一道题:给出一个网格图,其中某些格子有宝物,每次从左上角出发,只能向下或右走,问至少走多少次才能将宝藏捡完。但这个问题对老胡来说太简单了,现在假设每个格子中有好多宝物,而每一次经过一个格子至多只能捡走一个,至少走多少次才能把宝物全部捡完。
输入:
第一行为正整数T,代表数据组数。
每组数据第一行为正整数N,M代表网格图有N行M列,接下来N行每行M个非负整数,表示此格子中宝物数量,0代表没有
输出:
输出一个整数,表示至少要走多少次。
样例输入:
1
3 3
0 1 5
5 0 0
1 0 0
样例输出:
10
数据规模:
30%: N,M<=5
50%: N,M<=100
100%: N,M<=1000,每个格子中宝物数不超过10^6
方法: Dilworth定理:DAG的最小链覆盖=最大点独立集
然而半天过不了样例...i,j循环顺序打反了...不会的同学搜一下Dilworth定理吧(O3不开也可以)
1 #pragma GCC optimize(3)//可以删掉,打读入优化的话完全不会TLE 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 #include<cstdlib> 7 #include<ctime> 8 #include<cmath> 9 using namespace std; 10 inline int read(){ 11 int ans=0,f=1;char chr=getchar(); 12 while(!isdigit(chr)){if(chr=='-')f=-1;chr=getchar();} 13 while(isdigit(chr)) {ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();} 14 return ans*f; 15 int x;scanf("%d",&x); 16 return x; 17 }const int M=1005; 18 int n,m,f[M][M]; 19 int main(){ 20 freopen("treasure.in","r",stdin); 21 freopen("treasure.out","w",stdout); 22 int T=read(); 23 while(T--){ 24 n=read(),m=read(); 25 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)f[i][j]=read(); 26 for(int j=1;j<=m;j++) 27 for(int i=n;i>0;i--) 28 f[i][j]=max(f[i][j]+f[i+1][j-1],max(f[i][j-1],f[i+1][j])); 29 printf("%d\n",f[1][n]); 30 } 31 return 0; 32 }
T3配对
题目描述:
给出一棵n个点的树,将这n个点两两配对,求所有可行的方案中配对两点间的距离的总和最大为多少。
输入:
一个数n(n保证为偶数)。
接下来n-1行每行三个数x,y,z表示有一条长度为z的边连接x和y。
输出:
一个数表示答案。
样例输入:
6
1 2 1
1 3 1
1 4 1
3 5 1
4 6 1
样例输出:
7
样例解释:
配对方案:(1,2)(3,4)(5,6)
数据规模:
30%: N<=10
50%: N<=100
70%: N<=1000
100%: N<=10^5,z<=10^9
比赛的时候打了一个模拟退火+树剖(+线段树求距离)LCA(打算骗70分,噢真是可笑...),然后没开long long成功爆0,否则20分(应该是参数没调好)...
正解:分开对每一条边考虑:若是要总值最大,那么一条边被计算的次数越多越好,然后记录一下每个子树的大小,取该点子树大小和除去该子树数其他部分大小的min值
文字看不懂的话就看代码吧...出题人ciki表示这是一道智障题,然而没有一个人拿分?
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<cstdlib> 6 #include<ctime> 7 #include<cmath> 8 #define int long long 9 using namespace std; 10 inline int read(){ 11 int ans=0,f=1;char chr=getchar(); 12 while(!isdigit(chr)){if(chr=='-')f=-1;chr=getchar();} 13 while(isdigit(chr)) {ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();} 14 return ans*f; 15 }const int M=1e5+5; 16 int n,x,y,z,p[M]; 17 long long ans; 18 int head[M<<1],nxt[M<<1],ver[M<<1],val[M<<1],tot,sz[M]; 19 inline void add(int x,int y,int z){ver[++tot]=y;val[tot]=z;nxt[tot]=head[x];head[x]=tot;} 20 const double delta=0.998,t_min=1e-14; 21 void dfs(int x,int fa){sz[x]=1; 22 for(int i=head[x];i;i=nxt[i]){int y=ver[i]; 23 if(y==fa) continue; 24 dfs(y,x);sz[x]+=sz[y]; 25 ans+=val[i]*min(sz[y],n-sz[y]); 26 } 27 } 28 signed main(){ 29 freopen("match.in","r",stdin); 30 freopen("match.out","w",stdout); 31 n=read(); 32 for(int i=1;i<n;i++){x=read(),y=read(),z=read();add(x,y,z),add(y,x,z);} 33 dfs(1,0);cout<<ans; 34 return 0; 35 }