本场感觉EF挺有意思。
const int maxn = 1e5 + 10;
typedef pair <int, int> PII;
int main()
{
int k;
scanf("%d", &k);
int m = 21 * 60;
m += k;
printf("%02d:%02d\n", m / 60, m % 60);
return 0;
}
void cal(int x, int y)
{
for(int j = 0 ; j < 8 ; j ++)
{
ll temp = 0;
for(int i = 0 ; i < N ; i ++)
{
int cur_x = (x + i * dx[j] + N) % N;
int cur_y = (y + i * dy[j] + N) % N;
temp = temp * 10ll + mm[cur_x][cur_y] * 1ll;
}
maxx = max(maxx, temp);
}
}
int main()
{
scanf("%d", &N);
for(int i = 0 ; i < N ; i ++)
{
for(int j = 0 ; j < N ; j ++)
{
scanf("%1d", &mm[i][j]);
}
}
for(int i = 0 ; i < N ; i ++)
{
for(int j = 0 ; j < N ; j ++)
{
cal(i, j);
}
}
printf("%lld\n", maxx);
return 0;
}
给定一个字符串S,给定Q个查询。
1 x:把最后x个字符移到S前面。
2 x:输出此时第x个字符。
2 < = N < = 5 e 5 , 1 < = Q < = 5 e 5 , 1 < = x < = N 2 <= N <= 5e5, 1<= Q <= 5e5, 1 <= x <= N 2<=N<=5e5,1<=Q<=5e5,1<=x<=N
根据数据范围可知暴力铁T。
操纵1相当于把此时字符串的开头换成了len - x % len。
那么从现在开始第x个字符为str[len - x % len + x - 1]
int main()
{
int N, Q;
scanf("%d %d", &N, &Q);
string str;
cin >> str;
int len = str.length();
ll sum = 0;
while(Q --)
{
int t, x;
scanf("%d %d", &t, &x);
if(t == 1)
{
sum += x * 1ll;
}
else
{
int sta = len - sum % len;
x --;
cout << str[(sta + x) % len] << endl;
}
}
return 0;
}
给定N个关卡,对于第一次通关需要A[i],B[i]的时间,非第一次通关,A[i]可跳过。问通过X次需要的最少时间。
1 < = N < = 2 e 5 , 1 < = A [ i ] , B [ i ] < = 1 e 9 , 1 < = X < = 1 e 9 1 <= N <= 2e5, 1<= A[i], B[i] <= 1e9,1 <= X <= 1e9 1<=N<=2e5,1<=A[i],B[i]<=1e9,1<=X<=1e9
观察数据范围直接暴力铁T。
考虑假设最后第X次通关在关卡a,那么最优策略为[1, a]全是第一次通过,且在第a关重复通关即可。
ll sum[maxn], A[maxn], B[maxn];
int main()
{
int N, X;
scanf("%d %d", &N, &X);
for(int i = 1 ; i <= N ; i ++)
{
scanf("%lld %lld", &A[i], &B[i]);
}
ll ans = 2e18, sum = 0;
for(int i = 1 ; i <= N ; i ++)
{
sum += A[i] + B[i];
if(i >= X)
ans = min(ans, sum);
else
{
ans = min(ans, sum + (X - i) * B[i]);
}
}
printf("%lld\n", ans);
return 0;
}
有无数多土豆且给定权值序列0–N - 1,第i个土豆的权值为W[(i - 1) % N],最开始为1个空箱子,假设此时装进去的土豆权值>=X时,需要换一个新箱子。给定Q个查询,求第K个箱子中装了几个土豆。
1 < = N , Q < = 2 e 5 , 1 < = X < = 1 e 9 1 <= N, Q <= 2e5, 1 <= X <= 1e9 1<=N,Q<=2e5,1<=X<=1e9
1 < = w [ i ] < = 1 e 9 , 1 < = K [ i ] < = 1 e 12 1 <= w[i] <= 1e9,1 <= K[i] <= 1e12 1<=w[i]<=1e9,1<=K[i]<=1e12
考虑到K达到了1e12,肯定是找循环节。但是这里提供一种不需要找循环节的倍增解法。(qwq 可能是太懒了。)
题目要求出第K个箱子装多少个土豆。(且K特别大)
考虑倍增。
f[i][j].first:从i开始拿 2 j 2 ^ j 2j个箱子的下一次开始拿的下标
f[i][j].second:从i开始拿 2 j 2 ^ j 2j个箱子且第 2 j 2 ^ j 2j个箱子中的值。
f [ i ] [ j ] . f i r s t = f [ f [ i ] [ j − 1 ] . f i r s t ] [ j − 1 ] . f i r s t f[i][j].first = f[f[i][j - 1].first][j - 1].first f[i][j].first=f[f[i][j−1].first][j−1].first
f [ i ] [ j ] . s e c o n d = f [ f [ i ] [ j − 1 ] . f i r s t ] [ j − 1 ] . s e c o n d f[i][j].second = f[f[i][j - 1].first][j - 1].second f[i][j].second=f[f[i][j−1].first][j−1].second
可以提前预处理出f[i][0]。
那么每次查询时,只需要根据K的二进制位开始即可。
总的时间复杂度为O(nlogn + Qlogk)) 纯纯的暴力
但是需要注意预处理时出现特殊情况。二分第一个 >= X % sum。
要找到下一次开始拿的下标。
假设此时下标为 i i i且X % sum != 0, 说明此时下一次开始拿的下标为l + 1。
X % sum == 0, 说明下一次开始拿的下标为 i i i
ll w[maxn], sum[maxn], X, t, s;
PII f[maxn][55];
ll N, Q;
bool check(int l, int mid)
{
if(l == 0)
{
if(sum[mid] >= s) return true;
else return false;
}
else
{
if(sum[mid] - sum[l - 1] >= s) return true;
else return false;
}
}
void init()
{
t = X / sum[N - 1];
s = X % sum[N - 1]; //剩下0个
if(s == 0)
{
for(int i = 0 ; i < N ; i ++)
f[i][0].first = i, f[i][0].second = N * 1ll * t;
}
else
{
for(int i = 0 ; i < N ; i ++)
{
int l = i , r = 2 * N - 1;
while(l < r)
{
int mid = (l + r) >> 1;
if(check(i,mid)) r = mid;
else l = mid + 1;
} // l
f[i][0].first = (l + 1) % N;
f[i][0].second = N * 1ll * t + l * 1ll - i * 1ll + 1;
}
}
}
void ST()
{
init();
for(int j = 1 ; j <= 50 ; j ++)
{
for(int i = 0 ; i < N ; i ++)
{
f[i][j].first = f[f[i][j - 1].first][j - 1].first % N;
f[i][j].second = f[f[i][j - 1].first][j - 1].second;
}
}
}
ll query(ll k)
{
ll cur = 0, cur_w = 0;
for(ll i = 50 ; i >= 0 ; i --)
{
if(k >> i & 1)
{
k -= 1ll << i;
cur_w = f[cur][i].second;
cur = f[cur][i].first;
}
}
return cur_w;
}
int main()
{
scanf("%lld %lld %lld", &N ,&Q, &X);
for(int i = 0 ; i < N ; i ++)
{
scanf("%lld", &w[i]);
}
for(int i = N ; i < 2 * N ; i ++)
w[i] = w[i - N];
for(int i = 0 ; i < 2 * N ; i ++)
{
if(i == 0)
sum[i] = w[i];
else sum[i] = sum[i - 1] + w[i];
}
ST();
while(Q --)
{
ll k;
scanf("%lld", &k);
printf("%lld\n", query(k));
}
return 0;
}
在X-Y轴上,在x = n 和 y = n上有普通道路。在x = B * n 和 y = B * n上有高速道路。(n为整数)
对于当前点(x,y),每次可向当前上下左右四个方向移动。在高速道路上移动每步花费1s,否则花费Ks。
给定T组,求从(Sx, Sy)到(Gx, Gy)的最小时间。
1 < = T < = 2 e 5 , 1 < = B , K < = 1 e 9 1 <= T <= 2e5, 1 <= B , K <= 1e9 1<=T<=2e5,1<=B,K<=1e9
0 < = S x , S y , G x , G y < = 1 e 9 0 <= Sx, Sy, Gx, Gy <= 1e9 0<=Sx,Sy,Gx,Gy<=1e9
观察到范围较大。
要么是贪心,要么是建图最短路。
假设贪心。
对于在两个格子中的点且没有挨着。
S要到达G点。 考虑到最小时间
S肯定是要先走到高速道路上。那么对于G点,别的点要想到达,那么最优的选择(尽量走高速道路)也只有四个,从G号点的上下左右到达。
如果格子紧挨着。还是一样的。
考虑同一个格子。和上图一样
最优策略肯定是从4个方向上。
那么总的路径方案为 4 * 4种。
接下来只要需要计算在高速路上的两点之间的需要的最小时间。
可以发现存在cal()函数中三种方案。取min即可。
PII s[10], g[10];ll b, k, sx, sy, gx, gy;
void func(PII a[], ll x, ll y)
{
a[1] = {x, y + b - (y % b)}; //上
a[2] = {x, y - (y % b)}; // 下
a[3] = {x - (x % b), y}; // 左
a[4] = {x + b - (x % b), y}; // 右
}
ll cal(ll x1, ll y1, ll x2, ll y2)
{
ll temp = 2e18;
bool flog = false;
if(x1 / b == x2 / b && y1 % b == 0 && y2 % b == 0)
flog = true, temp = min({temp, abs(y1 - y2) * k + abs(x1 - x2), abs(y1 - y2) + b - x1 % b + b - x2 % b, abs(y1 - y2) + x1 % b + x2 % b});
if(y1 / b == y2 / b && x1 % b == 0 && x2 % b == 0)
flog = true,temp = min({temp, abs(x1 - x2) * k + abs(y1 - y2), abs(x1 - x2) + y1 % b + y2 % b, abs(x1 - x2) + b - y1 % b + b - y2 % b});
if(!flog) temp = min(temp, abs(x1 - x2) + abs(y1 - y2));
return temp;
}
int main()
{
int T;
scanf("%d", &T);
while(T --)
{
scanf("%lld %lld %lld %lld %lld %lld", &b, &k, &sx, &sy, &gx, &gy);
func(s, sx, sy), func(g, gx, gy);
ll res = (abs(sx - gx) + abs(sy - gy)) * k;
for(int i = 1 ; i <= 4 ; i ++)
{
for(int j = 1 ; j <= 4 ; j ++)
{
res = min(res, (abs(s[i].first - sx) + abs(s[i].second - sy) + abs(g[j].first - gx) + abs(g[j].second - gy)) * k + cal(s[i].first, s[i].second, g[j].first, g[j].second));
}
}
printf("%lld\n",res);
}
return 0;
}
给定一个简单无向图,N * N的邻接矩阵,求有多少个(i, j, k),(i < j < k <= N)
A[i][j] = 1, A[j][k] = 1, A[i][k] = 1
1 < = N < = 3000 1 <= N <= 3000 1<=N<=3000
暴力思路直接暴力三重for。
枚举i,j ,k。是否满足条件。 O(N ^ 3) 铁T。
考虑优化。
假设A[i][j]=1,那么找A[i][k] = 1且A[j][k] = 1即可。
没有感觉到特别像bitset嘛
用bitset优化一重for。 O(N ^ 3 / 32)
int A[5010][5010];
bitset <5010> f[maxn];
int main()
{
int N; // [i, j] [j, k] [i, k]
scanf("%d", &N);
for(int i = 1 ; i <= N ; i ++)
{
for(int j = 1 ; j <= N ; j ++)
scanf("%1d", &A[i][j]), f[i][j] = A[i][j];
}
ll res = 0;
for(int i = 1 ; i <= N ; i ++)
{
for(int j = i + 1 ; j <= N ; j ++)
{
if(f[i][j])
{
res += (f[i] & f[j]).count();
}
}
}
printf("%lld\n",res / 3);
return 0;
}