头大
这个暑假完就要去搞NOIP了。。。
暑假55天也就20次测试。。。为防万一我还是每次测试玩都写个总结。。
。。。我收回Test 3总结报告的话。。。我考完这次真的连怎么说自己都不知道怎么说了。。。被long=int+int阴了70分(否则应该是110)
问题是还是原题。。。。。。
Day1 (60/300)
T1 Math(30/100)
题目背景
SOURCE:NOIP2015-SHY-3
题目描述
给定 2 个数组 a[] 和 b[] ,他们有相同的长度 n ,你可以任意对 a[] 和 b[] 进行重排列,我们定义函数
x = Σ(a[i]*b[i])
请问 x 最大可以取到多少,最小可以取到多少?
输入格式
第一行一个数字 n ,表示数组的长度;
第二行 n 个整数,表示数组 a ;
第三行 n 个整数,表示数组 b 。
输出格式
输出两个整数表示答案。
样例数据 1
输入 [复制]
2
10 3
10 9
输出
127 120
备注
【样例说明】
Maximum:10 * 10 + 3 * 9 = 127
Minimum:10 * 3 + 10 * 9 = 120
【数据范围】
对 40% 的输入数据 :n≤10
对 100% 的输入数据 :n≤100000,1≤a[i], b[i]≤100000
本来用一个什么什么定理就能搞定的,代码极短而且还不需要用高精度,最后因为没有ans = (long long) a[i]*b[i]就gg了。。。
无语。
STD.CPP
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<ctime>
using namespace std;
int n,a[100500],b[100500];
unsigned long long maxx=0,minn=0;
int main()
{
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) cin >> b[i];
sort(b+1,b+n+1);
for(int i=1;i<=n;i++)
{
maxx += (long long)a[i]*b[i];
minn += (long long)a[n-i+1]*b[i];
}
cout << maxx << " " << minn << endl;
return 0;
}
T2 Shortest(30/100)
题目背景
SOURCE:NOIP2015-SHY-3
题目描述
给定一张 n 个点的有向带权完全图,和一个数组 a[] ,请按顺序删除数组中的点,请求出在删除点 a[i] 以前,所有未删除点对之间的最短路的值的和。
输入格式
第一行一个整数 n ,表示点数;
接下来 n 行,每行 n 个数构成邻接矩阵,描述每条边的权值,保证 i 号点到 i 号点的权值为 0 ;
最后一行 n 个小于等于 n 的不同的数,描述数组 a[]。
输出格式
输出 1 行 n 个数,表示答案。
样例数据 1
输入 [复制]
4
0 3 1 1
6 0 400 1
2 4 0 1
1 1 1 0
4 1 2 3
输出
17 23 404 0
备注
【数据范围】
对 30% 的输入数据 :1≤n≤10 ;
对 100% 的输入数据 :1≤n≤500;0<权值≤100000 。
MY.CPP
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<ctime>
using namespace std;
int u,v,ai[505];
long long ans[505];
long long mar[505][505];
bool visit[505];
int n,tot=0,first[505],p[505];
long long dis[505];
bool exist[505];
struct node{
int x;
int y;
long long w;
int next;
}side[250050];
void addedge(int x,int y,long long w)//建立邻接表(数组方式)
{
side[++tot].x=x;
side[tot].y=y;
side[tot].w=w;
side[tot].next=first[x];
first[x]=tot;
}
long long spfa(int s,int t) //主程序,SPFA
{
memset(exist,0,sizeof(exist));
memset(dis,127,sizeof(dis)); //初始化最短距离为一个较大的值
int head=0,tail=1; //头指针,尾指针
dis[s]=0; //起点的最短距离赋值为0
p[1]=s; //起点t入队
exist[s]=true; //做入队标记
while(head<tail)
{
head++;
exist[p[head]]=false;
for(int u=first[p[head]];u!=0;u=side[u].next)
{
#define i1 (dis[p[head]]+side[u].w)
#define i2 (dis[side[u].y])
if(i1 < i2) //如果能更新终点的最短路
{
i2 = i1; //则更新该终点的最短路
if(!exist[side[u].y]) //如果该终点不在队列中
{
tail++; //队尾指针递增
p[tail]=side[u].y; //把更新了最短路的终点v[u]入队
exist[side[u].y]=true; //作入队标记
}
}
}
}
return dis[t];
}
int main()
{
memset(ans,0,sizeof(ans));
cin >> n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin >> mar[i][j];
for(int i=n;i>=1;i--)
cin >> ai[i];
for(int i=1;i<=n;i++)
{
visit[ai[i]] = true;
for(int j=1;j<=i;j++)
{
addedge(ai[i],ai[j],mar[ai[i]][ai[j]]);
addedge(ai[j],ai[i],mar[ai[j]][ai[i]]);
}
for(int a=1;a<=i;a++)
for(int b=1;b<=i;b++)
ans[n-i+1] += spfa(ai[a],ai[b]);
}
for(int i=1;i<=n;i++) cout << ans[i] << " ";
}
这道题告诉我们:不要因为你鄙视Floyed的复杂度而抛弃它选Dijkstra或者因纯粹的爱国心两者都扔而去选SPFA。实践证明考虑选择求最短路的模板时应该遵循先F再D后S的顺序,除非题目给的图是和八爪鱼一样的放射形才首选S(SPFA求链的最短路很快)
于是我就GG了。orz
STD.CPP
//卧槽是Floyed
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<ctime>
using namespace std;
int n;
long long a[505];
long long f[505][505],ans[505];
int main()
{
cin >> n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin >> f[i][j];
for(int i=1;i<=n;i++)
cin >> a[i];
for(int i=n;i>=1;i--)
{
for(int j=n;j>i;j--)
for(int k=n;k>i;k--)
{
f[a[i]][a[j]] = min(f[a[i]][a[j]],f[a[i]][a[k]]+f[a[k]][a[j]]);
f[a[j]][a[i]] = min(f[a[j]][a[i]],f[a[j]][a[k]]+f[a[k]][a[i]]);
}
for(int j=n;j>i;j--)
for(int k=n;k>i;k--)
f[a[j]][a[k]] = min(f[a[j]][a[k]],f[a[j]][a[i]]+f[a[i]][a[k]]);
for(int j=n;j>=i;j--)
for(int k=n;k>=i;k--)
ans[i] += f[a[j]][a[k]];
}
for(int i=1;i<=n;i++) cout << ans[i] << " ";
}
3.Half(0/100)
题目背景
SOURCE:NOIP2015-SHY-1
题目描述
给定 n 个数,求最大的数 m ,使得 m 是 n 个数中至少一半的数的约数。
注意:m 不一定在 n 个数中,只要满足要求即可。
输入格式
第一行一个整数 n ,表示数组大小。
第二行 n 个整数,表示数组的 n 个元素。
输出格式
输出一个整数,表示答案。
样例数据 1
输入 [复制]
6
6 2 3 4 5 6
输出
3
备注
【样例说明】
3 是 6、3、6 的约数,达到了一半的要求;
【数据范围】
对 40% 的输入数据 : n≤100;
对 100% 的输入数据 :n≤100000;1≤数字的大小≤10^12。
奇特的gcd与随机枚举,我还要找时间研究研究。
STD.CPP
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<ctime>
using namespace std;
int n,cnt;
long long a[1000001],c[1000001],v[1000001],f[1000001];
long long gcd(long long a,long long b)
{
if(!b) return a;
return gcd(b,a%b);
}
int find(int n,long long aim)
{
if(v[n]==aim) return n;
int left=0,right=n,mid=(left+right)>>1;
for(;left+1<right;mid=(left+right)>>1)
if(v[mid]<aim) left=mid;
else right=mid;
return right;
}
int main()
{
cin >> n;
for(int i=1;i<=n;i++)
cin >> a[i];
srand(time(0)); random_shuffle(a+1,a+n+1);
long long ans = 0;
for(int i=1;i<=n&&i<=10;i++)
{
cnt = 0;
sort(c+1,c+cnt+1);
int num=0;
for(long long j=1;j*j<=a[i];j++)
if(!(a[i]%j))
{
v[++num]=j; f[num]=0;
if(j*j!=a[i])
{
v[++num] = a[i]/j;
f[num] = 0;
}
}
sort(v+1,v+num+1);
for(int j=1;j<=n;j++)
++f[find(num,gcd(a[i],a[j]))];
for(int j=1;j<=num;j++)
{
long long total = 0;
if(v[j]<=ans) continue;
for(int k=j;k<=num;k++)
if(!(v[k]%v[j])) total+=f[k];
if(total*2>=n) ans = max(ans,v[j]);
}
}
cout << ans << endl;
}
代码一样短的吓人。。而且我一直以为不可能在我考完noip前会出现的随机大法就这样出现了。
想死的心都没了。