杭电多校第一场
1001 Hide-And-Seek Game
题意:给出一棵树,每次询问第一个人从Sa-Sb之间来回走,第二个人在Ta-Tb之间来回走,最早相遇的节点,如果无法相遇就输出-1
做法:由于数据量是1e3级别,因此对于每一条路径都可以使用暴力找祖先的方法求出路径,然后对于路径上的每一个节点,其出现的时间都是模上两倍路径长度后的某一个值或者某两个值,因此找到路径后问题就转换成x=a1(mod m1), x= a2(mod m2)的同余方程组的最小解问题,由于模m可能有两个值,因此最多有四个同余方程组,依次求解找最小值即可,求解可以使用扩展中国剩余定理。
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 3e3 + 10;
int n, m;
ll ai[5], mi[5];
int dep[maxn];
int fa[maxn];
vector<int>edges[maxn];
ll exgcd(ll a, ll b, ll &x, ll &y){
if(b == 0){
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a/b * x;
return d;
}
ll excrt(){
ll x, y;
ll m1 = mi[1], a1 = ai[1];
ll ans = 0;
ll a2 = ai[2], m2 = mi[2];
ll a = m1, b = m2, c = (a2 - a1 % m2 + m2) % m2;
ll d = exgcd(a , b, x , y);
if(c % d != 0) return -1;
x = x * (c / d) % (b / d);
ans = a1 + x * m1;
m1 = m2 / d * m1;
ans = (ans % m1 + m1) % m1;
return ans;
}
ll lcm(ll a, ll b){
return (a * b) / __gcd(a, b);
}
void dfs(int u, int fath){
fa[u] = fath;
dep[u] = dep[fath] + 1;
for(auto v : edges[u]){
if(v != fath){
dfs(v, u);
}
}
}
int lca(int u, int v){
if(dep[u] > dep[v]) swap(u, v);
while(dep[v] != dep[u]) v = fa[v];
if(u == v) return u;
while(fa[u] != fa[v]){
u = fa[u];
v = fa[v];
}
return fa[u];
}
void solve(){
cin>>n>>m;
for(int i = 0; i <= n; i++) edges[i].clear();
for(int i = 1; i <= n - 1; i++){
int u, v;
cin>>u>>v;
dep[i] = 0;
edges[u].push_back(v);
edges[v].push_back(u);
}
dep[n] = 0;
dfs(1, 0);
while(m--){
int sa, sb, ta, tb;
cin>>sa>>sb>>ta>>tb;
vector<int>path1;
vector<int>path2;
vector<int>tmp;
int u = lca(sa, sb);
while(sa != u){
path1.push_back(sa);
sa = fa[sa];
}
path1.push_back(u);
while(sb != u){
tmp.push_back(sb);
sb = fa[sb];
}
reverse(tmp.begin(), tmp.end());
for(auto x : tmp){
path1.push_back(x);
}
tmp.clear();
u = lca(ta, tb);
while(ta != u){
path2.push_back(ta);
ta = fa[ta];
}
path2.push_back(u);
while(tb != u){
tmp.push_back(tb);
tb = fa[tb];
}
reverse(tmp.begin(), tmp.end());
for(auto x : tmp){
path2.push_back(x);
}
tmp.clear();
tmp = path1;
reverse(tmp.begin(), tmp.end());
int len = tmp.size();
for(int i = 1; i < len - 1; i++) path1.push_back(tmp[i]);
tmp.clear();
tmp = path2;
reverse(tmp.begin(), tmp.end());
len = tmp.size();
for(int i = 1; i < len - 1; i++) path2.push_back(tmp[i]);
ll mod1 = path1.size();
ll mod2 = path2.size();
map<int ,int>mp[3];
for(int i = 0; i < mod1; i++){
if(!mp[1][path1[i]]) mp[1][path1[i]] = (i + 1) % mod1;
else mp[2][path1[i]] = (i + 1) % mod1;
}
ll mina = 1e16;
int ans = -1;
for(int i = 0; i < mod2; i++){
int a = (i + 1) % mod2;
if(mp[1].count(path2[i])){
ai[1] = mp[1][path2[i]];
mi[1] = mod1;
ai[2] = a;
mi[2] = mod2;
ll res = excrt();
if(res != -1){
if(res == 0){
res = lcm(mod1, mod2);
}
if(res < mina){
mina = res;
ans = path2[i];
}
}
}
if(mp[2].count(path2[i])){
ai[1] = mp[2][path2[i]];
mi[1] = mod1;
ai[2] = a;
mi[2] = mod2;
ll res = excrt();
if(res != -1){
if(res == 0){
res = lcm(mod1, mod2);
}
if(res < mina){
mina = res;
ans = path2[i];
}
}
}
}
cout<<ans<<"\n";
}
}
int main(){
cin.tie(0);
ios::sync_with_stdio(false);
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}
1005 Cyclically Isomorphic
题意:给n个长度为m的字符串,给出q个询问,每次询问第x和第y个字符串是否能够通过循环右移相等
做法:用最小表示法求循环右移过程中字典序最小的字符串,然后比较这些字符串是否相等即可
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, m, q;
string s[maxn];
string getmin(string x){
string res;
int n = x.size();
x = x + x;
int i = 0, j = 1;
int len = x.size();
while(j < n){
int k = 0;
while(k < n - 1 && x[i + k] == x[j + k]){
k++;
}
if(x[i + k] > x[j + k]){
i += k + 1;
}
else{
j += k + 1;
}
if(i == j) j++;
if(i > j) swap(i, j);
}
for(int p = i; p <= i + n - 1; p++){
res += x[p];
}
return res;
}
void solve(){
cin>>n>>m;
for(int i = 1; i <= n; i++){
cin>>s[i];
s[i] = getmin(s[i]);
}
cin>>q;
while(q--){
int x, y;
cin>>x>>y;
if(s[x] == s[y]) cout<<"Yes\n";
else cout<<"No\n";
}
}
int main(){
cin.tie(0);
ios::sync_with_stdio(false);
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}
1009 Assertion
题意:鸽巢原理
做法:判断m *(d - 1)和n的大小关系即可
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void solve(){
ll n, m, d;
cin>>n>>m>>d;
if((d - 1) * n < m){
cout<<"Yes\n";
}else{
cout<<"No\n";
}
}
int main(){
cin.tie(0);
ios::sync_with_stdio(false);
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}
1010 Easy problem I
题意:给n个数,有m次询问,有两种操作,第一种将[l,r]上的每个a[i],让a[i] = abs(a[i] - x),第二种则是询问[l,r]的区间和。并且x的给出是递增的。
做法:可以发现,对于每个a[i],假设当前为x[i], a[i] = x[i] - a[i] < x[i] < x[i + 1],因此当一个数小于给出的x之后,每次对这个数的操作都是乘上-1再加上x,否则就是每次都减去x,可以考虑建两棵线段树,第一棵树用来维护x < a[i]的情况,第二棵用来维护x > a[i]的情况,并且由于从第一棵树转到第二棵树之后的不可能再转回来,因此考虑在第二棵树上单点修改暴力转移,复杂度O(nlogn),剩下的部分就是线段树的区间加、区间乘和单点改的操作。
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 2e5 + 10;
const ll inf = 1e16;
int n, m;
ll a[maxn];
struct info1{
ll sum, mn, cnt;//通过最小值来对点进行分类
};
struct info2{
ll sum, cnt;
};
struct tag2{
ll mul, add;
};
info2 operator+(info2 a, info2 b){
return info2{
a.sum + b.sum,
a.cnt + b.cnt
};
}
info2 operator+(info2 a, tag2 tag){
return info2{
a.sum * tag.mul + a.cnt * tag.add,
a.cnt
};
}
tag2 operator+(tag2 a, tag2 b){
return tag2{
a.mul * b.mul,
a.add * b.mul + b.add
};
}
info1 operator+(info1 a, info1 b){
return info1{
a.sum + b.sum,
min(a.mn, b.mn),
a.cnt + b.cnt
};
}
info1 operator+(info1 a, int tag){
return info1{
a.sum + tag * a.cnt,
a.mn + tag,
a.cnt
};
}
struct tree2{
struct node{
int l, r;
info2 info;
tag2 tag;
}tr[maxn * 4];
void pushtag(int p, tag2 tag){
tr[p].info = tr[p].info + tag;
tr[p].tag = tr[p].tag + tag;
}
void pushdown(int p){
if(tr[p].tag.mul != 1 || tr[p].tag.add != 0){
pushtag(p << 1, tr[p].tag);
pushtag((p << 1) + 1, tr[p].tag);
tr[p].tag = {1, 0};
}
}
void build(int p, int l, int r){
tr[p] = {l, r};
tr[p].info = {0, 0};
tr[p].tag = {0, 0};
if(l == r) return;
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build((p << 1) + 1, mid + 1, r);
}
void update(int p, int pos, ll val){
if(tr[p].l == tr[p].r){
tr[p].info = {val, 1};
return;
}
pushdown(p);
int mid = (tr[p].l + tr[p].r ) >> 1;
if(pos <= mid) update(p << 1, pos, val);
else update((p << 1) + 1, pos , val);
tr[p].info = tr[p << 1].info + tr[(p << 1) + 1].info;
}
info2 query(int p, int ql, int qr){
if(tr[p].l >= ql && tr[p].r <= qr) return tr[p].info;
pushdown(p);
int mid = (tr[p].l + tr[p].r) >> 1;
if(qr <= mid) return query(p << 1, ql, qr);
if(ql > mid) return query((p << 1) + 1, ql, qr);
return query(p << 1, ql, qr) + query((p << 1) + 1, ql, qr);
}
void modify(int p, int l, int r, ll mul, ll add){
if(tr[p].l >= l && tr[p].r <= r){
pushtag(p, tag2{mul, add});
return;
}
pushdown(p);
int mid = (tr[p].l + tr[p].r) >> 1;
if(l <= mid) modify(p << 1, l, r, mul, add);
if(r > mid) modify((p << 1) + 1, l, r, mul, add);
tr[p].info = tr[p << 1].info + tr[(p << 1) + 1].info;
}
}t2;
struct tree1{
struct node{
int l, r;
info1 info;
ll tag;
}tr[4 * maxn];
void pushtag(int p, ll tag){
tr[p].info = tr[p].info + tag;
tr[p].tag += tag;//标记叠加
}
void pushdown(int p){
if(tr[p].tag){
pushtag(p << 1, tr[p].tag);
pushtag((p << 1) + 1, tr[p].tag);
tr[p].tag = 0;
}
}
void build(int p, int l, int r){
tr[p] = {l, r};
tr[p].tag = 0;
if(l == r){
tr[p].info = {a[l], a[l], 1};
return;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build((p << 1) + 1, mid + 1, r);
tr[p].info = tr[p << 1].info + tr[(p << 1) + 1].info;
}
void update(int p, int l, int r, ll val){
if(tr[p].l >= l && tr[p].r <= r){
//如过当前区间的最小值大于要减的值,那么就等价于当前区间减去val
if(tr[p].info.mn > val){
pushtag(p, -val);
return;
}
//执行以下程序说明最小值小于要减去的值,那么此时就会涉及到区间部分值需要取反
if(tr[p].l == tr[p].r){
t2.update(1, tr[p].l, val - tr[p].info.mn);
tr[p].info = {0, inf, 0};
return;
}
pushdown(p);
update(p << 1, l, r, val);
update((p << 1) + 1, l, r, val);
tr[p].info = tr[p << 1].info + tr[(p << 1) + 1].info;
return;
}
pushdown(p);
int mid = (tr[p].l + tr[p].r) >> 1;
if(l <= mid) update((p << 1), l, r, val);
if(r > mid) update((p << 1) + 1, l, r, val);
tr[p].info = tr[p << 1].info + tr[(p << 1) + 1].info;
}
info1 query(int p, int ql, int qr){
if(tr[p].l >= ql && tr[p].r <= qr) return tr[p].info;
pushdown(p);
int mid = (tr[p].l + tr[p].r) >> 1;
if(qr <= mid) return query(p << 1, ql, qr);
if(ql > mid) return query((p << 1) + 1, ql, qr);
return query(p << 1, ql, qr) + query((p << 1) + 1, ql, qr);
}
}t1;
void solve(){
cin>>n>>m;
for(int i = 1; i <= n; i++){
cin>>a[i];
}
t1.build(1, 1, n);
t2.build(1, 1, n);
while(m--){
int op;
cin>>op;
if(op == 1){
int l, r, x;
cin>>l>>r>>x;
t2.modify(1, l, r, -1, x);
t1.update(1, l, r, x);
}
else{
int l ,r;
cin>>l>>r;
ll ans = t1.query(1, l, r).sum + t2.query(1, l, r).sum;
cout<<ans<<"\n";
}
}
}
int main(){
cin.tie(0);
ios::sync_with_stdio(false);
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}
标签:tmp,杭电多校,return,int,ll,tr,while,补题,第一场
From: https://www.cnblogs.com/pang-bai/p/17574902.html