赛时 rank 9 ,T1 100,T2 100,T3 0,T4 0
update: 赛后T1被hack了,成了99,死因没有记忆化
挂成了rank 10
我又要挂分了。
T3 暴力挂了?
话说jjdw和k8啥时候回来。
T1 Fate
签到题,最短路板子。
考虑一个点\(a\),其最短路前驱节点为\(b\),前驱结点组成的集合为\(B\),其次短路的前驱节点为\(c\),组成的集合为\(C\)
那么考虑对于每一个\(c\),看看其是否与\(B\)中的节点有且仅有一条边相连,若有,则答案数加上c的最短路路径数。
记搜一下
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
#include<sys/timeb.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
#define pii pair<int,int>
#define mk make_pair
const int N = 2e5 + 10,mod = 1e9 + 7;
struct EDGE{int to,next;}edge[N<<1];
int head[N],cnt;
inline void add(int u,int v){edge[++cnt] = {v,head[u]};head[u] = cnt;}
priority_queue<pii,vector<pii>,greater<pii> > q;
bitset<N> vis;
unordered_set<int> pre[N];
int dist[N],num[N];
int n,s,t,m,mn;
inline void dijkstra(int s){
vis.reset();
memset(dist,0x3f,sizeof dist);
dist[s] = 0;num[s] = 1;
queue<int> q;q.push(s);vis[s] = true;
while(q.size()){
int x = q.front();q.pop();vis[x] = false;
for(int i = head[x]; i;i = edge[i].next){
int y = edge[i].to;
if(dist[y] > dist[x] + 1){
dist[y] = dist[x] + 1;
num[y] = num[x] % mod;
unordered_set<int> ().swap(pre[y]);
pre[y].insert(x);
if(!vis[y]){
vis[y] = true;
q.push(y);
}
}
else if(dist[y] == dist[x] + 1) num[y] = (num[y] + num[x]) % mod,pre[y].insert(x);
}
}
}
int ans = 0;
int mp[N];
int dfs(int x){
if(vis[x]) return mp[x];
vis[x] = true;
int ans = 0;
for(int i = head[x]; i;i = edge[i].next){
int y = edge[i].to;
if(pre[x].find(y) != pre[x].end()) continue;
if(dist[y] == dist[x]) ans = (0ll + ans + num[y]) % mod;
}
for(auto i : pre[x]) ans = (ans + dfs(i)) % mod;
return mp[x] = ans;
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
cin>>n>>m>>s>>t;
for(int i = 1,u,v;i <= m; ++i){
cin>>u>>v;
add(u,v);add(v,u);
}
dijkstra(s);;
cout<<dfs(t)<<'\n';
}
T2 EVA
贪心。
考虑枚举任意一点为起点,看看其在t时最多可以抓住多少。
用优先队列存一条鱼可以被抓住的起始时间,不能被抓住的终止时间。
然后pop就行
所以这也配蓝?
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
const int N = 2e3 + 10;
const db eps = 1e-9;
struct node{int x,v,w;}a[N];
#define mk make_pair
int n,s,sum;
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
cin>>n>>s;
for(int i = 1;i <= n; ++i) cin>>a[i].w>>a[i].x>>a[i].v;
sort(a+1,a+1+n,[](node x,node y){return x.x < y.x;});
int ans = 0;
for(int i = 1;i <= n; ++i){
__gnu_pbds::priority_queue<pair<db,int>,greater<pair<db,int> > > add;
__gnu_pbds::priority_queue<pair<db,int>,greater<pair<db,int> > > del;
// cerr<<i<<":\n";
int res = a[i].w;//算上自己
for(int j = 1;j <= n; ++j){
if(i == j) continue;
if(j < i){
if(a[j].v <= a[i].v && a[j].x != a[i].x) continue;//在后面还无法超过
else if(a[j].v == a[i].v && a[j].x == a[i].x){res += a[j].w;continue;}//在一起
else if(a[j].v < a[i].v && a[j].x < a[i].x) continue;
else if(a[j].v < a[i].v && a[j].x == a[i].x){
db reach = 1.0 * (a[i].x - a[j].x)/(a[j].v - a[i].v);
add.push(mk(reach,j));
del.push(mk(reach,j));
continue;
}
db reach = 1.0 * (a[i].x - a[j].x)/(a[j].v - a[i].v);//可以捉到
db de = 1.0 * (a[i].x + s - a[j].x)/(a[j].v - a[i].v);//最后可以捉到的时间
add.push(mk(reach,j)),del.push(mk(de,j));
}
if(j > i){
if(a[j].v >= a[i].v && a[i].x + s < a[j].x) continue;//无法捉到
else if(a[j].v == a[i].v && a[i].x + s >= a[j].x){res += a[j].w;continue;}//一直可以捉到
if(a[i].v > a[j].v){
db reach = 1.0 * (a[j].x - a[i].x - s)/(a[i].v - a[j].v);
db de = 1.0 * (a[j].x - a[i].x) / (a[i].v - a[j].v);
add.push(mk(reach,j));
del.push(mk(de,j));
}
else{
db reach = 0.0;
db de = 1.0 * (a[i].x + s - a[j].x)/(a[j].v - a[i].v);
add.push(mk(max(reach,0.0),j));
del.push(mk(de,j));
}
}
}
ans = max(ans,res);
while(add.size()){
db now = add.top().first;res += a[add.top().second].w;
add.pop();
while(del.size() && del.top().first + eps < now){
res -= a[del.top().second].w;
del.pop();
}
ans = max(ans,res);
}
}
cout<<ans<<'\n';
}
T3 嘉然登场
来自int_R的思路
考虑将所有的数分为两种\(<\frac{k}{2}\)的,\(\le\frac{k}{2}\)的。
将一个\(<\frac{k}{2}\)的数\(a_i\)放在第一个\(\le k-a_i\)上。
从大到小放所有\(\le\frac{k}{2}\)的数\(a_i\),放到一个数就把放在它上面的数也放了。
放第一类数\(a_i\le\frac{k}{2}\)的数会使得可以放的位置+1,放第二类数会使得可以放的位置-1。
除以重复元素出现次数的阶乘。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
const int N = 2e5 + 10,mod = 998244353;
int n,k,a[N],cnt[N];
inline ll power(ll a,ll b,ll mod){
ll res = 1;
for(; b;b >>= 1,a = a * a % mod)
if(b&1) res = res * a % mod;
return res;
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
cin>>n>>k;
for(int i = 1;i <= n; ++i) cin>>a[i];
sort(a+1,a+1+n);
if(a[n] + a[1] < k) return cout<<0<<'\n',0;
for(int i = 1;i <= n && a[i] <= (k-1)/2; ++i){
++cnt[lower_bound(a+1,a+1+n,k-a[i])-a];
}
ll ans = 1,pos_num = 1,total = 1;
for(int i = n;i && a[i] > (k-1)/2; --i){
ans = ans * pos_num % mod,++pos_num;
for(int j = 1;j <= cnt[i];++j)
ans = ans * pos_num % mod,--pos_num;
}
for(int i = 1,j = 1;i <= n; i = j){
while(a[j] == a[i]) ++j;
for(int k = 1;k <= j-i; ++k) total = total * k % mod;
}
cout<<ans * power(total,mod-2,mod)%mod;
}
T4 Clannad
P9340 [JOISC 2023 Day3] Tourism
赛时想了一个bitset的做法,但是莫名WA了。
套路题
考虑如何计算点集。我们用数据结构标记一下所有被查询的点走到某一个特殊点所经过的边,统计被标记的边的数量。
我们钦定这个特殊点为1,记\(LCA = lca\{c_i\}(i\in [l,r])\),多出来的答案就是\(dep_{LCA}-dep_1\)
考虑如何统计答案。
离线,按r排序,以时间为值进行覆盖。统计\(LCA\)子树内值\(\ge l\)的数的个数。
但是如果只覆盖到\(LCA\)的话不太好处理,那么我们直接覆盖到1,反正多出来的部分会被减去。
用一个BIT统计值域,ODT区间赋值。
其实还有bitset和回滚莫队的做法,但我不会……
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
const int N = 1e5 + 10;
int n,m,q,a[N],st[18][N],ans[N];;
struct Query{int l,r,id;}Q[N];
struct EDGE{int to,next;}edge[N<<1];
int head[N],cnt;
inline void add(int u,int v){edge[++cnt] = {v,head[u]};head[u] = cnt;}
namespace TCS{
int fa[N],dep[N],top[N],siz[N],son[N],dfn[N],tot;
void dfs1(int x){
siz[x] = 1;
dep[x] = dep[fa[x]] + 1;
for(int i = head[x]; i;i = edge[i].next){
int y = edge[i].to;
if(y == fa[x]) continue;
fa[y] = x;dfs1(y);
siz[x] += siz[y];
if(siz[son[x]] < siz[y]) son[x] = y;
}
}
void dfs2(int x,int t){
top[x] = t;
dfn[x] = ++tot;
if(son[x]) dfs2(son[x],t);else return;
for(int i = head[x]; i;i = edge[i].next){
int y = edge[i].to;
if(y == son[x] || y == fa[x]) continue;
dfs2(y,y);
}
}
inline int LCA(int x,int y){
int fx = top[x],fy = top[y];
while(fx != fy){
if(dep[fx] < dep[fy]) swap(fx,fy),swap(x,y);
x = fa[fx];
fx = top[x];
}
if(dep[x] > dep[y]) swap(x,y);
return x;
}
}using namespace TCS;
class BIT{
private:
int tree[N];
#define lowbit(x) (x&(-x))
public:
int mx;
inline void update(int pos,int val){
for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;
}
inline int query(int pos){
int res = 0;
for(int i = pos; i;i -= lowbit(i)) res += tree[i];
return res;
}
}T;
class ODT{
private:
struct node{
int l,r;mutable int val;
inline bool operator < (const node &a) const {return l < a.l;}
node (int L = 0,int R = 0,int Val = 0) : l(L),r(R),val(Val){}
};
#define sit set<node>::iterator
public:
set<node> s;
inline sit split(int pos){
sit it = s.lower_bound(node(pos));
if(it != s.end() && it->l == pos) return it;
--it;
int l = it->l,r = it->r,val = it->val;
s.erase(it);
s.insert(node(l,pos-1,val));
return s.insert(node(pos,r,val)).first;
}
inline void assign(int l,int r,int val){
sit it2 = split(r + 1),it1 = split(l);
for(sit it = it1;it != it2; ++it) T.update(m-it->val+1,-(it->r-it->l+1));
s.erase(it1,it2);
s.insert(node(l,r,val));
T.update(m-val+1,r-l+1);
}
inline void insert(int l,int r,int val){s.insert(node(l,r,val));}
}Tree;
inline void update(int x,int val){
while(x){
Tree.assign(dfn[top[x]],dfn[x],val);
x = fa[top[x]];
}
}
inline void pre(){
int t = log2(m)+1;
for(int i = 1;i <= m; ++i) st[0][i] = a[i];
for(int j = 1;j <= t; ++j){
for(int i = 1;i + (1<<j) - 1 <= m; ++i){
st[j][i] = LCA(st[j-1][i],st[j-1][i+(1<<(j-1))]);
}
}
}
inline int query(int l,int r){
int k = log2(r-l+1);
return LCA(st[k][l],st[k][r-(1<<k)+1]);
}
vector<int> vec[N];
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
cin>>n>>m>>q;Tree.insert(1,n,0);T.mx = m;
for(int i = 1,u,v;i < n; ++i) cin>>u>>v,add(u,v),add(v,u);
for(int i = 1;i <= m; ++i) cin>>a[i];
dfs1(1);dfs2(1,1);pre();
for(int i = 1;i <= q; ++i){
cin>>Q[i].l>>Q[i].r;Q[i].id = i;
vec[Q[i].r].push_back(i);
ans[i] = -dep[query(Q[i].l,Q[i].r)] + dep[1];
}
for(int i = 1;i <= m; ++i){
update(a[i],i);
for(int j : vec[i]) ans[j] += T.query(m-Q[j].l+1);
}
for(int i = 1;i <= q; ++i) cout<<ans[i]<<'\n';
}