The 15th Heilongjiang Provincial Collegiate Programming Contest(A,C,F,G,H,L)
比赛链接
2021/2/7训练赛
Problem.A August
题解
不难发现上半部分是个半径为\(a\)的圆,下半部分利用割补小正方形的方法得出等价于一个长为\(2a\),宽为\(2b\)的长方形。
比赛时没有发现,不过队友看出下半部分面积应该是个关于\(a\)和\(b\)的表达式,并且很好算,用答案减去圆的面积反推出面积为\(4ab\)。
代码
#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define pY puts("YES")
#define pN puts("NO")
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
#define outval2(a,b) cout << "Debuging...|" << #a << ": " << a <<"\t"<< #b << ": " << b << "\n";
#define outval3(a,b,c) cout << "Debuging...|" << #a << ": " << a <<"\t"<< #b << ": " << b <<"\t"<< #c << ": " << c << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
inline int read(){
int s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*f;
}
void solve(){
int a=read(),b=read();
double ans=PI*a*a;
printf("%.8f\n",ans+4.0*a*b);
}
int main(){
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
//debug = 1;
#endif
//time_t beg, end;
//if(debug) beg = clock();
int T=read();
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
Problem.C Cornelia Street
题解
比赛时感觉时间复杂度不是特别好确定,而且极端数据不是特别好构造,就写了一法暴力枚举\(A\)串长度的做法,交之前测了几发大数据都过了,就交了,一发A。
时间复杂度好像类似于调和级数。
代码
#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define pY puts("YES")
#define pN puts("NO")
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
#define outval2(a,b) cout << "Debuging...|" << #a << ": " << a <<"\t"<< #b << ": " << b << "\n";
#define outval3(a,b,c) cout << "Debuging...|" << #a << ": " << a <<"\t"<< #b << ": " << b <<"\t"<< #c << ": " << c << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
inline int read(){
int s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*f;
}
const int N = 1e5+7;
void solve(){
string s;cin>>s;
int len=s.size();
rp(l,1,len){
string pa="";
rp(i,0,l-1) pa+=s[i];
string pb="";
int f=1;
int ff=0;
rp(i,l,len-1){
int num=1;
string t="";t+=s[i];
while(i+1<len&&num<l) i++,t+=s[i],num++;
if(t==pa){
if(f==2) f=3;
}
else if(pb.size()==0&&f==1){
pb=t;f=2;
}
else if(t==pb&&f==2) continue;
else {
if(f==3){
if(i==len-1){
string tt=pa.substr(0,num);
if(tt!=t){
ff=1;
break;
}
}
else{
ff=1;
break;
}
}
else{
ff=1;
break;
}
}
}
// outval2(l,ff);
if(!ff&&f!=1){
cout<<pa<<" "<<pb<<endl;
break;
}
}
}
int main(){
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
solve();
return 0;
}
F. False God
oj: CodeForces
题意
有一种棋,在一个地图上有一个“金”和若干个“步”。每回合“金”可以向6个方向移动,“步”可以向1个方向移动,每回合每个单位都不能不移动。如图所示:
不论是“金”走到了“步”的位置还是“步”走到了“金”的位置,当“金”与“步”接触时,“金”得1分,同一个“步”只能被得分1次。
现在给出若干个“步”和一个“金”得位置,求出“金”能获得得最大得分。
题解
“金”得分有两种情况:
- “金”走到了“步”的位置
- “步”走到了“金”的位置
当“金”向下走的时候,由于“步”只能向下走,所以只有情况1可以得分。其实“金”向下得分只有“金”下面有一个“步”和他相邻这一种情况,所以这种情况可以特判,接下来我们就不考虑“金”向下移动的情况。
下面我们按照“步”的y坐标值对所有的“步”进行升序排序。
我们假设“步”的坐标为 \((x,y)\),“金”的坐标为 \((x_0,y_0)\)。
排序之后 \(y\) 小于 \(y_0\) 的“步”是不可能被得分的。
以上两种情况得分后,“金”的位置是不同的,情况1得分的回合结束后,“金”和“步”不在同一个位置,而情况2得分的回合结束后,“金”和“步”在同一个位置。虽然这种位置变化对一个回合没有影响,但是会对后面的回合有影响。
由于“步”只能向下走,所以我们考虑一个贪心策略:令“金”的y坐标值尽可能小。这样才有可能接触到更多的“步”,也就是说当一个“步”同时满足情况1和情况2得分的时候我们使用情况2得分。
状态转移:
在一个回合中,当 \(|x-x_0|\le y-y_0\) 时,“步”可以同时使用情况1和情况2得分,当 \(|x-x_0|\le y-y_0+1\) 时,可以使用情况2得分。
我们保存每个点被得分时“金”的总得分和“金”的 \(y\) 坐标值,并使“金”的y坐标值尽可能小。
然后 \(O(n^2)\) 递推求解。
代码
#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
inline int read() {
int x(0), f(1); char ch(getchar());
while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
struct poi {
int x, y;
int px, py;
int val;
poi() {
val = 0;
}
poi(int x, int y): x(x), y(y) {
val = 0;
}
bool operator<(const poi &b) const {
if(y != b.y) return y < b.y;
else return x < b.x;
}
};
vector<poi> a;
LL n;
LL x, y;
void init() {
a.clear();
}
void sol() {
init();
n = read();
LL ans = 0;
_for(i, n) {
LL tx = read(), ty = read();
if(ty < y) {
if(tx == x && ty == y - 1) ++ans;
continue;
}
a.push_back(poi(tx, ty));
}
sort(a.begin(), a.end());
_for(i, a.size()) {
if(abs(x - a[i].x) <= a[i].y - y) {
a[i].py = a[i].y;
a[i].px = a[i].x;
++a[i].val;
}
if(abs(x - a[i].x) == a[i].y - y + 1) {
a[i].py = a[i].y + 1;
a[i].px = a[i].x;
++a[i].val;
}
}
for(int ne = 1; ne < a.size(); ++ne) {
for(int ol = 0; ol < ne; ++ol) {
if(abs(a[ne].x - a[ol].px) <= a[ne].y - a[ol].py) {
if(a[ol].val + 1 > a[ne].val) {
a[ne].py = a[ne].y;
a[ne].px = a[ne].x;
a[ne].val = a[ol].val + 1;
}
else if(a[ol].val == a[ne].val) a[ne].py = a[ne].y;
}
if(abs(a[ne].x - a[ol].px) == a[ne].y - a[ol].py + 1) {
if(a[ol].val + 1 > a[ne].val) {
a[ne].py = a[ne].y + 1;
a[ne].px = a[ne].x;
a[ne].val = a[ol].val + 1;
}
}
}
}
LL tem = 0;
_for(i, a.size()) tem = max(tem, a[i].val);
printf("%lld\n", ans + tem);
}
int main() {
freopen("in.txt", "r", stdin);
int T = read();
_for(i, T) {
x = read(), y = read();
sol();
}
return 0;
}
Problem.G Goodbye
题解
手推几个样例可以发现规律:
首先对\(n\)进行唯一分解,并维护出最大的两个质因子。
1.当\(n\)为质数或者\(1\)时,输出\(0\)。
2.当质因子个数小于等于2时,输出\(-1\)。
3.否则答案就是这两个最大的质因子的乘积。
原因:首先为了保证\(Chino\)必胜,只能给\(David\)剩下两个质因子的乘积。
而又需要保证\(Chino\)的第一轮取的值最大,根据贪心的原则选取最大的两个质因子的乘积是最优策略。
代码
// #pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 100005;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;
inline int read() {
int x(0), f(1); char ch(getchar());
while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
struct poi {
};
int n;
vector<int> arr;
int vis[1000006];
void doit(int maxnum) {
for(int i = 2; i <= maxnum; ++i) {
if(!vis[i]) arr.push_back(i);
for(int j = 0; j < arr.size() && arr[j] * i <= maxnum; ++j) {
vis[arr[j] * i] = 1;
if(i % arr[j] == 0) break;
}
}
}
void init() {
}
void sol() {
init();
if(n == 1) {
printf("0\n");
return;
}
int a = 0, b = 0;
int x = n;
for(int i = 0; i < arr.size() && arr[i] <= x; ++i) {
// dg outval(arr[i]);
while(x % arr[i] == 0) {
if(arr[i] > a) {
b = a;
a = arr[i];
}
else if(arr[i] > b) b = arr[i];
x /= arr[i];
}
}
if(a == n) {
printf("0\n");
return;
}
if(b == 0 || n == a * b) {
printf("-1\n");
return;
}
// dg outval(a), outval(b);
printf("%d\n", a * b);
}
int main() {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
debug = 1;
#endif
time_t beg, end;
if(debug) beg = clock();
doit(100000);
int T = read();
_for(i, T) {
n = read();
sol();
}
// _rep(i, 1, 20) {
// n = i;
// sol();
// }
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
return 0;
}
Problem.H Hate That You Know Me
题解
这个题首先需要知道两个结论:
1.约数个数和等于数论分块和
即\(\sum_{i=1}^n\sigma_0(i)=\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor\)。
2.约数和等于数论分块和的变形
即\(\sum_{i=1}^n\sigma_1(i)=\sum_{i=1}^ni\times\lfloor\frac{n}{i}\rfloor\)。
那么我们不难得出约数平方和以及约数立方和的公式:
约数平方和
即\(\sum_{i=1}^n\sigma_2(i)=\sum_{i=1}^ni^{2}\times\lfloor\frac{n}{i}\rfloor\)。
约数立方和
即\(\sum_{i=1}^n\sigma_3(i)=\sum_{i=1}^ni^{3}\times\lfloor\frac{n}{i}\rfloor\)。
那么把公式转换以后不难发现第二个公式可以直接用数论分块求出答案。
对数论分块不懂的话请参考该链接。
题目中给的取余是自然溢出,我们直接用unsigned long long 维护答案即可。
trick: 注意要保证能够整除再进行除运算,否则直接用下取整会使得答案偏小。
代码
#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define ll unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define pY puts("YES")
#define pN puts("NO")
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
#define outval2(a,b) cout << "Debuging...|" << #a << ": " << a <<"\t"<< #b << ": " << b << "\n";
#define outval3(a,b,c) cout << "Debuging...|" << #a << ": " << a <<"\t"<< #b << ": " << b <<"\t"<< #c << ": " << c << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
inline int read(){
int s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*f;
}
ll f1(ll x){
ll a=x,b=x+1;
if(a%2==0) a/=2;
else b/=2;
return a*b;
}
ll f2(ll x){
ll a=x,b=x+1,c=2*x+1;
if(a%2==0) a/=2;
else b/=2;
if(a%3==0) a/=3;
else if(b%3==0) b/=3;
else if(c%3==0) c/=3;
return a*b*c;
}
ll f3(ll x){
ll a=x,b=x+1;
if(a%2==0) a/=2;
else b/=2;
return a*b*a*b;
}
ll calc(ll l,ll r,ll op){
if(op==0) return r-l+1ll;
else if(op==1) return f1(r)-f1(l-1);
else if(op==2) return f2(r)-f2(l-1);
else return f3(r)-f3(l-1);
}
void solve(){
ll a,b,n;cin>>a>>b>>n;
if(a==b){
puts("0");
return ;
}
ll ans1=0;
for(ll l = 1, r = 0; l <= n; l=r+1) {
r = n / (n / l);
ans1+=(n/l)*calc(l,r,a);
}
ll ans2=0;
for(ll l = 1, r = 0; l <= n; l=r+1) {
r = n / (n / l);
ans2+=(n/l)*calc(l,r,b);
// outval3(l,r,calc(l,r,b));
}
// outval2(ans1,ans2);
cout<<(ans1^ans2)<<endl;
}
int main(){
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
//debug = 1;
#endif
//time_t beg, end;
//if(debug) beg = clock();
int T=1;
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
Problem.L Let's Get Married
题解
可以发现是按照\(bfs\)的顺序进行放的。
那么我们对于每次的\(1\)操作,二分枚举得出\(id\)的层数,并可以找规律得出具体坐标。
对于每次的\(2\)操作,也可以通过找规律得出其编号。
代码
// #pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<LL, LL>
#define _for(i, a) for(LL i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(LL i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
// #define scl(x) scanf("%lld", &x)
// #define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const LL maxn = 100005;
const LL maxm = 1000005;
const LL maxp = 30;
const LL inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
LL debug = 0;
inline LL read() {
LL x(0), f(1); char ch(getchar());
while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
LL n;
inline void print(LL x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
print(x / 10);
putchar(x % 10 + '0');
}
pair< LL, LL > getV(LL id) {
if(id <= 4) return m_p(0, 0);
LL l = 1, r = 1e9, ans = l;
while(l <= r) {
LL mid = (l + r) /2;
if((4 * mid + 4) * mid / 2 <= id) {
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
return m_p(ans, (4 * ans + 4) * ans / 2);
}
int main() {
// ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
// debug = 1;
#endif
n = read();
LL nx = 0, ny = 0;
_for(i, n) {
LL op = read(), x, y;
if(op == 1) {
LL id = read();
if(id == 0) {
x = 0, y = 0;
}
else {
pair<LL, LL> t = getV(id);
dg outval(t.first), outval(t.second);
if(id - t.second <= 2 * t.first + 1) {
if((id - t.second) == 0) {
x = 0, y = t.first + 1;
}
else if((id - t.second)%2==1LL) {
x = -(id - t.second) / 2, y = t.first + 1 - (id - t.second) / 2;
}
else {
x = (id - t.second) / 2, y = t.first + 1 - (id - t.second) / 2;
}
}
else if((id - t.second) <= 2 * t.first + 1 + t.first + 2) {
LL nol = t.second + 2 * t.first + 2;
x = t.first + 1 - (id - nol), y = -(id - nol);
}
else {
LL nod = t.second + 2 * t.first + 2 + t.first + 1;
x = -(id - nod), y = -(t.first + 1 - (id - nod));
}
}
dg printf("x:%lld\ty:%lld\n", x, y);
print(x - nx);
printf(" ");
print(y - ny);
printf("\n");
}
else {
x = read(), y = read();
if(x == 0 && y == 0) {
printf("0\n");
}
else {
LL tf = abs(x) + abs(y) - 1;
LL ts = (4 * tf + 4) * tf / 2;
if(x == 0 && y > 0) print(ts + 1), printf("\n");
else if(x > 0 && y > 0) {
print(ts + 2 * x); printf("\n");
}
else if(x < 0 && y > 0) {
print(ts + 1 - 2 * x); printf("\n");
}
else if(x >= 0 && y <= 0) {
LL nol = ts + 2 * tf + 2;
print(nol - y); printf("\n");
}
else {
LL nod = ts + 2 * tf + 2 + tf + 1;
print(nod - x); printf("\n");
}
}
}
nx = x, ny = y;
}
return 0;
}