寒假就要结束了,马上就要上网课了,剩下的时间就要复习和预习大一下的知识了,博客可能暂时停更了,谢谢大家的观看。武汉加油!中国加油!
今天是寒假集训的最后一次考试了,题很不错,思维题较多,涉及算法的并不多,感谢jwGG以及实验室的学长学姐们亲情出题,谢谢cy老师和jwGG的假期培训。
map计数器的应用,因为他要计算相反数和他本身的和,所以0的时候要考虑。
AC代码:
#include
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int n;
map<pair<int, int>, int> a;
pair<int, int> q;
int main()
{
scanf("%d", &n);
while (n--)
{
int x, y;
scanf("%d%d", &x, &y);
if (a.count({-1 * x, -1 * y}) != 0) //如果它的相反数存在,就直接计算进去
{
a[{-1 * x, -1 * y}]++;
}
else //若他的相反是不存在,就计算它本身
a[{x, y}]++;
}
int cnt = 0;
for (map<pair<int, int>, int>::iterator i = a.begin(); i != a.end(); ++i)
{
if ((*i).second % 2 == 0)
cnt += (*i).second;
}
printf("%d\n", cnt);
return 0;
}
前缀和+快慢指针尺取法的应用,暴力也可以。
AC代码:
#include
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 300002;
int n, k;
int a[maxn];
bool flag = 0;
int main()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
a[i] = a[i - 1] + x; //前缀和
}
int j = 1;
for (int i = 0; i <= n && j <= n;) //快慢指针
{
if (a[j] - a[i] < k)
j++;
else if (a[j] - a[i] > k)
i++;
else
{
printf("%d %d", i + 1, j);
flag = 1;
break;
}
}
if (flag == 0)
puts("tiangeniupi");
return 0;
}
数学公式的推导+审题,一开始没审清题WA了两发。因为数据范围的情况,要开unsigned long long。
AC代码:
#include
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int main()
{
int t;
ll a[20] = {0, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, 9999999999, 99999999999, 999999999999, 9999999999999, 99999999999999, 999999999999999, 9999999999999999, 99999999999999999, 999999999999999999};
scanf("%d", &t);
while (t--)
{
unsigned long long cnt = 0;
unsigned long long x, y;
scanf("%llu%llu", &x, &y);
for (int i = 1; i <= 18; i++)
{
if (a[i] <= y)
++cnt;
if (a[i] > y)
break;
}
cnt *= x;
printf("%llu\n", cnt);
}
return 0;
}
最简单的签到题,set的应用。
AC代码:
#include
using namespace std;
int n, m;
int x;
set<int> a, b;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &x);
a.insert(x);
}
for (int i = 1; i <= m; i++)
{
scanf("%d", &x);
b.insert(x);
}
int cnt = 0, flag = 0;
for (set<int>::iterator it = a.begin(); it != a.end(); it++)
{
if (!b.count(*it))
{
printf("%d\n", *it);
cnt++;
flag = 1;
}
}
if (!flag)
puts("So crazy!!!");
return 0;
}
太坑了。一开始我想了三种方法:第一种:叉积+直线重合,可以避免计算时的精度问题,但是比较难优化,只能暴力。第二种:斜率+截距的方法,可以进行map计数优化,但是计算时具有精度问题。第三种,还是斜率+截距,但是斜率用最简的式表示,不用直接相除。
但最后,各种权衡之下,我还是选择了第一种,然后就TLE,哎。
晚上看完jwGG讲解后,标程就是选择的第二种方法,那我就来说说第二组方法的题解吧。
选择map进行计数 :在斜率存在时分别记录斜率、y轴截距;在斜率不存在时,也就是垂直的时候,记录垂直的条数和x轴截距。
在输入坐标的时候进行更新答案。
直线与直线的关系有三种:第一种:重合;第二种:相交;第三种:平行。虽然直线重合,但是这个也计算相交。我们只需要把当前录入的所有直线 - 与当前直线平行的直线的数量。平行的直线=相同的斜率数量 - 相同的截距数量。
a n s + = i − 1 − ( m a p [ 斜 率 ] − m a p [ 截 距 ] ) ans+=i-1-(map[斜率]-map[截距]) ans+=i−1−(map[斜率]−map[截距])
AC代码:
#include
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 100002;
int n, x[3], y[3];
double td, tk;
map<double, int> k; //斜率->数量
map<pair<double, double>, int> d; //斜率,截距->数量,重合
map<int, int> d2; //垂直时候,截距->数量,重合
int cnt = 0; //垂直总个数
ll ans = 0;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= 2; j++)
scanf("%d%d", &x[j], &y[j]);
if (x[1] == x[2]) //斜率不存在情况
{
td = x[1]; //x截距
d2[td]++;
cnt++;
ans += (i - 1 - (cnt - d2[td]));
}
else
{
tk = (y[2] - y[1]) * 1.0 / (x[2] - x[1]); //k
td = y[1] - tk * x[1]; //y截距
k[tk]++;
d[{tk, td}]++;
ans += (i - 1 - (k[tk] - d[{tk, td}]));
}
}
printf("%lld\n", ans);
return 0;
}
字符串的模拟题,先打解码表,最难想到的时解码表的打法,还有反推的过程。
d [ x ] [ z ] = y d[x][z]=y d[x][z]=y
AC代码:
#include
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 100002;
pair<int, int> p[maxn];
char a[55], b[130][130];
string s[1005];
void init()
{
for (int i = 0; i <= 51; i++)
{
if (i <= 25)
a[i] = 'A' + i;
else
a[i] = 'a' + i - 26;
}
for (int i = 0; i <= 51; i++) //x
for (int j = 0; j <= 51; j++) //y
{
int z = (i + j) % 52; //替换后的z,b中是存字符对应的ASCLLII
b[a[i]][a[z]] = a[j];
}
}
int main()
{
ios::sync_with_stdio(false);
int n, m;
init();
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> p[i].first >> p[i].second;
for (int i = 1; i <= n; i++)
cin >> s[i];
for (int i = m; i >= 1; i--)
{
int y = p[i].second;
int x = p[i].first;
int len1 = s[x].size(), len2 = s[y].size();
for (int i = 0; i < len2; i++)
s[y][i] = (b[s[x][i % len1]][s[y][i]]);
}
for (int i = 1; i <= n; i++)
cout << s[i] << endl;
return 0;
}
暂时未作出,后续更新…
一道披着二分的暴力查找答案的题,直接放出题人的题解。
为什么直接暴力不用二分在时间上是可行的吧,因为我看所有通过代码都写的枚举容量的上界是sum,但是如果上界真的是sum的话在时间上理论上是不可行的,这一点上并不是我数据出水了,是它的下界可以视为max(ceil(sum / k),maxV),上界为ceil(sum / k)+MAXV 所以枚举次数为二者之差远达不到sum次,最多也不会超过1000次所以从下界while(1)向上寻找在时间上是可行的。
AC代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int t, n, m;
int v[1002];
bool vis[1002];
bool check(int tt)
{
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= m; i++)
{
int sum = 0;
for (int j = 1; j <= n; j++)
{
if (sum + v[j] <= tt && vis[j] == 0)
{
sum += v[j];
vis[j] = 1;
}
}
}
for (int i = 1; i <= n; i++)
{
if (vis[i] == 0) //no
return 0;
}
return 1;
}
int main()
{
scanf("%d", &t);
int k = 0;
while (t--)
{
int sum = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &v[i]);
sum += v[i];
}
sort(v + 1, v + 1 + n, greater<int>());
int l = sum / m, r = sum, ans;
for (int i = l; i <= r; i++)
{
if (check(i))
{
ans = i;
break;
}
}
printf("Case #%d: %d\n", ++k, ans);
}
return 0;
}