A.The beautiful values of the palace
题意:
给定一个阶的螺旋矩阵,其中
个点是有价值的,
个询问,每次询问求出
和
组成的矩形内的价值
题解:
通过分析推出公式可以的算出螺旋矩阵每一个点的价值,先求出目标块在哪一圈层,然后判断在所在圈的哪一侧边,分类计算即可。对于求值本质是一个二位前缀和,但是考虑到复杂度求出二位前缀和显然是行不通的。可以对点按x坐标进行升序,这样可以保证
这样我们只要求出小于等于
的值之和即可。可以用树状数组或者主席树解决。
方法一:主席树
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
int t, n, m, p, root[N], cnt;
int x1, x2, yz1, yz2;
vector<int> vx, vy;
struct nod {
int x, y, w;
bool operator<(const nod &a)const {
return x < a.x;
}
} no[N];
struct node {
int l, r;
ll sum;
} zxs[N * 40];
ll getn(ll x, ll y)
{
ll t = min(min(x, y), min(n - x + 1, n - y + 1));
ll ta = 4 * (t - 1) * (n - t + 1);
if (x == n - t + 1) ta += n - t - y + 2;//在所在圈层的右侧边
else if (x == t) ta += 2 * n - 5 * t + y + 3;//左侧边
else if (y == t) ta += 2 * n - 3 * t - x + 3;//下侧边
else ta += 3 * n - 7 * t + x + 4;//上侧边
return ta;
}
ll cal(ll x) {
ll sum = 0;
while (x)
sum += x % 10, x /= 10;
return sum;
}
void add(int l, int r, int pre, int &now, int pos, ll num) {
zxs[++cnt] = zxs[pre], now = cnt, zxs[cnt].sum += num;
if(l == r)
return;
int m = (l + r) >> 1;
if(pos <= m)
add(l, m, zxs[pre].l, zxs[now].l, pos, num);
else
add(m + 1, r, zxs[pre].r, zxs[now].r, pos, num);
}
ll query(int pl, int pr, int l, int r, int L, int R) {
if(pl <= l && r <= pr)
return zxs[R].sum - zxs[L].sum;
ll m = (l + r) >> 1, ans = 0;
if(pl <= m)
ans += query(pl, pr, l, m, zxs[L].l, zxs[R].l);
if(pr > m)
ans += query(pl, pr, m + 1, r, zxs[L].r, zxs[R].r);
return ans;
}
int main() {
scanf("%d", &t);
while(t--) {
cnt = 0;
memset(root, 0, sizeof root);
scanf("%d%d%d", &n, &m, &p);
vx.clear(), vy.clear();
for(int i = 1; i <= m; i++) {
scanf("%d%d", &no[i].x, &no[i].y);
no[i].w = cal(getn(no[i].x, no[i].y));
vx.push_back(no[i].x), vy.push_back(no[i].y);
}
sort(no + 1, no + m + 1);
sort(vx.begin(), vx.end());//vx不能erase,因为下面二分查询的是vx的位置,erase后位置少了就不准了。
sort(vy.begin(), vy.end()), vy.erase(unique(vy.begin(), vy.end()), vy.end());
int sz = vy.size();
for(int i = 1; i <= m; i++) {
int ny = lower_bound(vy.begin(), vy.end(), no[i].y) - vy.begin() + 1;
add(1, sz, root[i - 1], root[i], ny, no[i].w);
}
while(p--) {
//由于y坐标离散化的是他们的值,所以相同值离散化后是一样的
scanf("%d%d%d%d", &x1, &yz1, &x2, &yz2);//下面二分的是位置,lx,ly要用lower_bound计算离散化后的位置
int lx = lower_bound(vx.begin(), vx.end(), x1) - vx.begin() + 1;
int ly = lower_bound(vy.begin(), vy.end(), yz1) - vy.begin() + 1;
//这里用upper_bound是为了查找下一个位置,防止和lx相同
int rx = upper_bound(vx.begin(), vx.end(), x2) - vx.begin();
//这里的upper_bound也是查找下一个位置,和x一样,但不能用
//lower_bound(vy.begin(), vy.end(), no[i].y) - vy.begin() + 1;
//因为如果这样的话,当yz2恰好是一个没有点在上面的坐标,所以要找离它最近的而且比它小的坐标,
//若比它大,则多计算了就
int ry = upper_bound(vy.begin(), vy.end(), yz2) - vy.begin();
printf("%lld\n", query(ly, ry, 1, sz, root[lx - 1], root[rx]));
}
}
return 0;
}方法二:树状数组
#include <bits/stdc++.h>
#define maxn 1000005
#define lowbit(x) ((x)&(-x))
#define _for(i, a) for(LL i = 0; i < (a); i++)
#define _rep(i, a, b) for(LL i = (a); i <= (b); i++)
typedef long long LL;
using namespace std;
struct poi {
LL x, y, flag, pos, val;
poi() {}
poi(LL x, LL y, LL flag, LL pos, LL val) :x(x), y(y), flag(flag), pos(pos), val(val) {}
bool operator < (poi t) {
if (x != t.x) return x < t.x;
if (y != t.y) return y < t.y;
return pos < t.pos;
}
};
LL getval(LL x, LL y, LL n) {
LL t = min(min(x, y), min(n - x + 1, n - y + 1));
LL ta = 4 * (t - 1) * (n - t + 1);
if (x == n - t + 1) ta += n - t - y + 2;//在所在圈层的右侧边
else if (x == t) ta += 2 * n - 5 * t + y + 3;//左侧边
else if (y == t) ta += 2 * n - 3 * t - x + 3;//下侧边
else ta += 3 * n - 7 * t + x + 4;//上侧边
return ta;
}
LL T[maxn * 2];
LL getnum(LL x, LL n) {
LL ans = 0;
while (x > 0) {
ans += T[x];
x -= lowbit(x);
}
return ans;
}
void upd(LL x, LL y, LL n) {
while (x <= n) {
T[x] += y;
x += lowbit(x);
}
}
LL n, m, p;
LL cnt = 0;
poi a[maxn * 2];
LL ans[maxn];
void init() {
//初始化
memset(T, 0, sizeof(T));
memset(ans, 0, sizeof(ans));
cnt = 0;
}
int main() {
//freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
LL T;
cin >> T;
while (T--) {
init();
cin >> n >> m >> p;
_for(i, m) {
//求出点的美丽值 并把点压入队列
LL x, y;
cin >> x >> y;
LL tt = getval(x, y, n), _tem = 0;
while (tt) {//求出美丽值
_tem += tt % 10;
tt /= 10;
}
a[cnt++] = poi(x, y, 0, i, _tem);
}
_rep(i, m, m + p - 1) {
//把区间拆解为4个前缀和,一块压入队列,用flag区分是加还是减
LL x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
a[cnt++] = poi(x1 - 1, y1 - 1, 1, i, 0);
a[cnt++] = poi(x2, y1 - 1, -1, i, 0);
a[cnt++] = poi(x1 - 1, y2, -1, i, 0);
a[cnt++] = poi(x2, y2, 1, i, 0);
}
sort(a, a + cnt);
//以时间来区分x,以树状数组来区分y
_for(i, cnt) {
if (a[i].pos < m) {
//说明是点,不是区间
upd(a[i].y, a[i].val, n);
}
else {
//是区间,要我们求前缀和
ans[a[i].pos - m] += getnum(a[i].y, n) * a[i].flag;
}
}
_for(i, p) {
cout << ans[i] << "\n";
}
}
return 0;
}B.super_log
题意:
求最小的正整数x,使得
题解:
通过将递归式展开,展开次等于
,所以
为
(共
次)。根据欧拉降幂公式
。对
进行重写则可以将
与
当作互素,从而不用讨论b与
的大小关系。
要注意这样做:
1、 快速幂和cal函数都要换成;
2、 最终答案需要模。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1001000;
int phi[maxn] = {0, 1};
ll mm(ll x, ll m)
{
return x < m ? x : x % m + m;
}
ll pow64(ll a, ll b, ll m)
{
ll result = 1;
while (b)
{
if (b & 1)
result = mm(result * a, m);
a = mm(a * a, m);
b >>= 1;
}
return result;
}
void caleuler()
{
for (int i = 2; i < maxn; i++)
if (!phi[i])
for (int j = i; j < maxn; j += i)
{
if (!phi[j])
phi[j] = j;
phi[j] = phi[j] / i * (i - 1);
}
}
int t, a, b, m;
int calc(int a, int b, int m)
{
if (m == 1)
return 1;
if (!b)
return 1;
int p = phi[m];
int x = calc(a, b - 1, p);
return pow64(a, x, m);
}
int main()
{
caleuler();
scanf("%d", &t);
for (int i = 0; i < t; i++)
{
scanf("%d%d%d", &a, &b, &m);
printf("%d\n", calc(a, b, m) % m);
}
return 0;
} D. Robots
题意:
给定一个个点
条边的有向图,现在要从
号结点走到
号结点。对于每个点,其走向下一个点与在当前点停留不动的概率均等,第
天的花费是
,问走到第
号结点的期望是多少
题解:
由于走向下一个点与在当前点停留不动的概率均等,那么假设点的出度为
,其停留不动与走向其一个邻接点的概率为
假设从号点走到
号点的期望时间为
,那么花费为
,可分为
与
两部分
表示
结点到
的期望花费时间,
表示
结点的平方期望花费时间
又由
因此
化简可得
因此从开始反向建图,求一遍拓扑排序即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int deg[maxn];
double dp1[maxn], dp2[maxn], p[maxn];
vector<int> G[maxn];
void Topsort(int s)
{
stack<int> st;
st.push(s);
while (!st.empty())
{
int u = st.top();
st.pop();
for (auto v : G[u])
{
deg[v]--;
dp1[v] += p[v] * (dp1[u] + 1.0);
dp2[v] += p[v] * (dp2[u] + 2 * dp1[u] + 1.0);
if (!deg[v])
{
dp2[v] += p[v] * (2 * dp1[v] + 1);
st.push(v);
}
}
}
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
dp1[i] = 0, dp2[i] = 0, p[i] = 0, deg[i] = 0, G[i].clear();
for (int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
G[v].push_back(u);
deg[u]++;
}
for (int i = 1; i <= n; i++)
{
if (deg[i])
{
p[i] = 1.0 / deg[i];
dp1[i] += p[i];
}
}
Topsort(n);
printf("%.2lf\n", dp1[1] / 2.0 + dp2[1] / 2.0);
}
return 0;
} F.Greedy Sequence
题意:
给定一个的序列
,要求构造递增序列
,要求
,
,同时要求字典序最大,求每个序列
的长度
题解:
从开始,用
记录
的长度,那么对于
只需要从
到
找到最大
的满足
,
。同时发现,找到最大的
只需要用线段树
query(1,1,n,i-k,i+k)即可
暴力:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
map<int, int> mp;
int f[maxn];
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n, k;
scanf("%d%d", &n, &k);
mp.clear();
for (int i = 1, x; i <= n; i++)
{
scanf("%d", &x);
mp[x] = i;
}
for (int i = 1; i <= n; i++)
{
f[i] = 1;
for (int j = i - 1; j >= 1; j--)
{
if (abs(mp[i] - mp[j]) <= k)
{
f[i] += f[j];
break;
}
}
}
for (int i = 1; i <= n; i++)
printf("%d%c", f[i], i == n ? '\n' : ' ');
}
return 0;
}线段树:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int n, k, m, l, r, now, sum;
int node[maxn << 2], lazy[maxn << 2];
void pushup(int rt)
{
node[rt] = max(node[rt << 1], node[rt << 1 | 1]);
}
void build(int rt, int l, int r)
{
node[rt] = 0;
if (l == r)
{
return;
}
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushup(rt);
}
void update(int rt, int l, int r, int pos, int val)
{
if (l == r)
{
node[rt] = val;
return;
}
int mid = l + r >> 1;
if (pos <= mid)
update(rt << 1, l, mid, pos, val);
if (pos > mid)
update(rt << 1 | 1, mid + 1, r, pos, val);
pushup(rt);
}
int query(int rt, int l, int r, int L, int R)
{
if (L <= l && r <= R)
return node[rt];
int mid = l + r >> 1;
int ans = 0;
if (L <= mid)
ans = max(ans, query(rt << 1, l, mid, L, R));
if (mid < R)
ans = max(ans, query(rt << 1 | 1, mid + 1, r, L, R));
return ans;
}
map<int, int> mp;
int f[maxn];
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n, k;
scanf("%d%d", &n, &k);
mp.clear();
build(1,1,n);
for (int i = 1, x; i <= n; i++)
{
scanf("%d", &x);
mp[x] = i;
}
for (int i = 1; i <= n; i++)
{
f[i] = 1;
int x = query(1,1,n,mp[i]-k,mp[i]+k);
f[i]+=f[x];
update(1,1,n,mp[i],i);
}
for (int i = 1; i <= n; i++)
printf("%d%c", f[i], i == n ? '\n' : ' ');
}
return 0;
}H.Holy Grail
题意:
给出一个个点,
条边的有向图,再给
组询问,每组询问要求在
间建一条权值最小的边,使图中没有负环。题目保证有解。
题解:
保证有解那么原来图中不会存在负环,那么负环只可能在添加新边的时候产生,因此边的最小边权就是
最短路的相反数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 505;
const int maxm = 2005;
const ll INF = 1ll << 40;
int n, m;
struct Edge
{
int to, next;
ll w;
} edge[maxm];
int head[maxn], tot;
inline void init()
{
for (int i = 0; i < maxm; i++)
edge[i].next = 0;
for (int i = 0; i < maxn; i++)
head[i] = -1;
tot = 0;
}
inline void addedge(int u, int v, ll w)
{
edge[tot].to = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot++;
}
queue<int> q;
ll dis[maxn];
int inq[maxn];
void spfa(int u)
{
for (int i = 0; i < maxn; i++)
dis[i] = INF;
dis[u] = 0;
inq[u] = 1;
q.push(u);
while (!q.empty())
{
int x = q.front();
q.pop();
inq[x] = 0;
for (int i = head[x]; ~i; i = edge[i].next)
{
int y = edge[i].to;
ll z = edge[i].w;
if (dis[y] > dis[x] + z)
{
dis[y] = dis[x] + z; //更新最短路
if (!inq[y])
{
inq[y] = 1;
q.push(y);
}
}
}
}
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
init();
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v;
ll w;
scanf("%d%d%lld", &u, &v, &w);
addedge(u, v, w);
}
for (int i = 1; i <= 6; i++)
{
int u, v;
scanf("%d%d", &u, &v);
spfa(v);
addedge(u, v, -dis[u]);
printf("%lld\n", -dis[u]);
}
}
return 0;
} 
京公网安备 11010502036488号