T1
王牌校长波波牛开店啦,他的店里有 n 个商品,每个商品有两个属性,基准价格 ai 和波动 bi。
由于波波牛不喜欢一成不变的事物,他决定每天对商品的价格进行调整,具体地,在第 k 天的时候,波波牛可以将第 i 个商品的价格调整成任意一个满足 |ai−x|≤k×bi 的整数 x。
波波牛喜欢整齐如一的事物,所以他希望知道在最早第几天之后,他可以使得所有商品的价格相等。
基准价格不会改变。
输入格式
第一行一个整数 n,表示序列长度。
第二行 n 个整数,第 i 个整数表示 ai。
第三行 n 个整数,第 i 个整数表示 bi。
输出格式
一行一个整数,表示答案。
思路
直接二分,每次判断所有区间是否有交即可
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+9;
int n,ans;
int a[N],b[N];
bool check(int mid){
int mx=-1e9,mr=1e9;
for (int i=1;i<=n;i++){
int mn=a[i]-mid*b[i];
int mxx=a[i]+mid*b[i];
mx=max(mx,mn);
mr=min(mr,mxx);
if (mx>mr) return false;
}
return true;
}
signed main()
{
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=n;i++) cin>>b[i];
int l=0,r=1e9;
while (l<=r) {
int mid=(l+r)>>1;
if (check(mid)){
ans=mid;
r=mid-1;
}
else{
l=mid+1;
}
}
cout<<ans;
return 0;
}
T2
王牌校长波波牛的家里种了一棵树,树上有 n 个点,点之间通过 n−1 条边相连,每条边有一个权值 wi∈{0,1,2,3}。
波波牛觉得这棵树太大了,于是他决定删除一些边,使得树分成若干个连通块。
但是简单的删除无法满足波波牛的探索欲望,他给每个连通块定义了一个贡献,即该连通块内所有边权的异或和,如果一个连通块内没有边,那么该连通块的贡献为 1。
他希望你帮他找到一种删除边的方案,使得所有连通块的贡献的乘积最大,由于乘积实在是太大了,他只想知道这个乘积对 998244353 取模的结果。
输入格式
第一行一个整数 n,表示树的大小。
接下来 n−1 行每行3个整数 ui,vi,wi,表示点 ui,vi 间有一条边权为 wi 的边。
输出格式
一行一个整数,表示最大连通块乘积对 998244353 取模的结果。
思路
用dp来维护,再由wi∈{0,1,2,3} 来把要使用高精度的问题转化
代码
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define mk make_pair
using namespace std;
const int N=2e5+9;
const int mod=998244353;
const double log23 = log2(3);
struct jd{
int v,w;
};
vector<jd> G[N];
pii f[N][10],g[N];
int n;
pii operator + (pii a,pii b){
pii cmp={-1,-1};
if(a==cmp||b==cmp) return mk(-1,-1);
return mk(a.first+b.first,a.second+b.second);
}
pii max(pii a,pii b) {
pii cmp={-1,-1};
if(a==cmp) return b;
if(b==cmp) return a;
if(a.first-b.first>=(b.second-a.second)*log23) return a;
else return b;
}
int qpow(int a,int b) {
int res=1;
while(b) {
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void dfs(int x,int fa){
// cout<<1;
f[x][0]=mk(0,0),f[x][1]=f[x][2]=f[x][3]=mk(-1,-1);
for (auto edge:G[x]){
int y=edge.v,w=edge.w;
if (y==fa) continue;
dfs(y,x);
vector<pii> h(10);
for (int i=0;i<4;i++){
h[i]=f[x][i]+g[y];
for (int j=0;j<4;j++){
h[i]=max(h[i],f[x][j]+f[y][w^i^j]);
}
}
for (int i=0;i<4;i++){
f[x][i]=h[i];
}
}
g[x]=max(max(f[x][0],f[x][1]),max(f[x][2]+mk(1,0),f[x][3]+mk(0,1)));
}
signed main()
{
cin>>n;
for (int i=1;i<=n-1;i++){
int u,v,w;
cin>>u>>v>>w;
G[u].push_back({v,w}),G[v].push_back({u,w});
}
dfs(1,0);
cout<<1ll*qpow(2,g[1].first)*qpow(3,g[1].second)%mod;
return 0;
}
T3
王牌校长波波牛在玩一个游戏,游戏规则如下:
初始有一个 1∼2n 的排列,每次按顺序取出一个数,然后猜测这个数与下一个数的大小关系,如果猜对了就继续抽数,如果猜错了就结束,直到排列中所有数都被抽走。
一局游戏的得分为抽出的牌数。
这个游戏有一个简单的策略,即如果抽出来的数 ≤n 就猜这个数比下一个数小,否则猜大。
波波牛想知道在排列随机的情况下,使用这个策略期望的得分是什么,你只需要输出该答案对给定质数 P 取模后的结果。
输入格式
第一行两个整数 n,P,分别表示排列长度与给定质数。
输出格式
一行一个整数,表示答案对 P 取模的结果。
思路
考虑 DP。设f[i][j][0/1]表示已经填了i个小数,j个大数,最后一段是小/大数的方案数,转移可以枚举下
一段包含多少个数。最后统计答案的时候枚举下一段的数个数为k,然后除开最接近n的数从k-1个
数中选一个放到最后
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1000;
int n,mod,ans;
int fac[N],f[N][N][2],c[N][N];
int qpow(int a,int b) {
int res=1;
while(b) {
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
signed main(){
cin>>n>>mod;
c[0][0]=1;
for (int i=1;i<=n;i++){
c[i][0]=1;
for (int j=1;j<=i;j++){
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
fac[0]=1;
for (int i=1;i<=n*2;i++){
fac[i]=fac[i-1]*i%mod;
}
f[0][0][0]=f[0][0][1]=1;
for (int i=0;i<=n;i++){
for (int j=0;j<=n;j++){
for (int op=0;op<2;op++){
if (f[i][j][op]){
int le=(!op) ? n-j : n-i;
for (int k=1;k<=le;k++){
if (!op) f[i][j+k][op^1]=(f[i][j+k][op^1]+(f[i][j][op]*c[le][k]%mod))%mod;
else f[i+k][j][op^1]=(f[i+k][j][op^1]+(f[i][j][op]*c[le][k]%mod))%mod;
}
for (int k=1;k<=le;k++){
ans=(ans+(f[i][j][op]*(i+j+k)%mod*c[le][k]%mod*(k-1)%mod*fac[2*n-i-j-k]%mod))%mod;
}
if (i==n&&j==n) ans=(ans+(f[i][j][op]*2ll*n%mod))%mod;
}
}
}
}
cout<<1ll*ans*qpow(fac[2*n],mod-2)%mod;
return 0;
}
T4
有一个长度为 L≤1012 的环,环上有 m≤2⋅105 个电脑,第 i 个电脑在 xi 位置,xi 两两不同,有 n≤2×105 个 oier 在环上,第 i 个 oier 会在 ti≤1015 时间出现在环上坐标为 pi 的位置,ti 两两不同,同时还会有一个参数 di≤1012 表示这个 oier 所需要的训练时间,以及一个方向 vi∈{0,1} 表示这个 oier 会顺时针走还是逆时针走。
因为要准备 csp,每个 oier 需要训练,每个 oier 会沿着他的方向每单位时间前进 1 格,当某个 oier 遇到一台电脑时,他占用这个电脑 di 的时间用来训练,在他占用的时间内这台电脑不能被其他人占用,当他训练结束后,他会释放电脑,并离开这个环,该电脑可以继续被其他人占用。若有 2 个 oier 同时到达一台电脑,那么出发时间早的 oier 占用这台电脑。
问每个 oier 在什么时候占用了电脑,占用了哪台电脑。
输入格式
第一行三个整数,分别表示 n,m,L。
第二行包含 m 个互不相同的整数 xi 表示电脑的位置。
接下来的 n 行每行包含四个整数 ti,pi,vi,di,分别表示第 i 个oier的出现时间、初始位置、方向、电脑占用时间。其中 vi 为 1 时表示oier沿正方向行走,为 0 时表示oier沿负方向行走。
输出格式
输出 n 行,每行包含2个整数表示第 i 个oier训练电脑的位置和开始训练的时间。
思路
动态维护最近发生的事件
1.oier出现,改变该oier运动方向上离他最近的一台电脑的最早到达的oier是谁
2.电脑出现,改变该电脑左右两边的oier的最近电脑
3.oier到达电脑,改变该位置左右两边的oier的最近电脑
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct event{
int time,pos,dur;
int type,dir,id;
bool operator<(const event &e) const{
if (time!=e.time) return time<e.time;
if (type!=e.type) return type<e.type;
if (type) return id<e.id;
else return pos<e.pos;
}
bool operator==(const event &e) const{
return time==e.time&&type==e.type&&pos==e.pos&&dur==e.dur&&dir==e.dir&&id==e.id;
}
};
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n,m,L;
cin>>n>>m>>L;
set<int> comput;
map<int,set<int>> oier_dir[2];
map<int,array<int,2>> comput_near;
for (int i=0;i<m;i++){
int x;
cin>>x;
comput.insert(x);
comput_near[x]={0,0};
}
set<event> q;
vector<array<int,4>> oier(n+1);
vector<event> oier_near(n+1);
for (int i=1;i<=n;i++){
int t,p,d,s;
cin>>t>>p>>s>>d;
oier_near[i].pos=-1;
if (s==1){
q.insert({t,p,d,1,1,i});
oier[i]={t,p,1,d};
} else{
q.insert({t,p,d,1,0,i});
oier[i]={t,p,0,d};
}
}
auto norm=[&](int x) {return (x%L+L)%L;};
auto dist=[&](int id,int pos,int ti) -> int{
if (pos==-1) return 1e18;
ti-=oier[id][0];
return oier[id][2]==1? norm(pos-ti-oier[id][1]) : norm(oier[id][1]-ti-pos);
};
auto find_right=[&](set<int> &st,int x) -> int{
if (st.empty()) return -1;
auto it=st.lower_bound(x);
if (it==st.end()) return *st.begin();
return *it;
};
auto find_left=[&](set<int> &st,int x) -> int{
if (st.empty()) return -1;
auto it=st.upper_bound(x);
if (it==st.begin()) return *st.rbegin();
return *prev(it);
};
auto find_right_id=[&](map<int,set<int>> &st,int x) -> int{
if (st.empty()) return -1;
auto it=st.lower_bound(x);
if (it==st.end()) return *st.begin()->second.begin();
return *it->second.begin();
};
auto find_left_id=[&](map<int,set<int>> &st,int x) -> int{
if (st.empty()) return -1;
auto it=st.upper_bound(x);
if (it==st.begin()) return *st.rbegin()->second.begin();
return *prev(it)->second.begin();
};
int cnt=0;
vector<pair<int,int>> ans(n+1);
auto link_oier=[&](int ti,int pos,int dur,int ty,int dir,int id){
int near=dir==1? find_right(comput,norm(pos+ti-oier[id][0])) : find_left(comput,norm(pos-ti+oier[id][0]));
if (near==-1) return;
int o=comput_near[near][!dir];
if (o==0){
comput_near[near][!dir]=id;
oier_near[id]={ti+dist(id,near,ti),near,dur,2,dir,id};
q.insert(oier_near[id]);
}
else{
if (make_pair(dist(id,near,ti),id)<make_pair(dist(o,near,ti),o)){
q.erase(oier_near[o]);
oier_near[o]={0,-1,0,0,0,0};
comput_near[near][!dir]=id;
oier_near[id]={ti+dist(id,near,ti),near,dur,2,dir,id};
q.insert(oier_near[id]);
}
}
};
while (cnt<n){
event e=*q.begin();
q.erase(q.begin());
if (e.type==0){
comput.insert(e.pos);
int left=find_left_id(oier_dir[1],norm(e.pos-e.time));
int right=find_right_id(oier_dir[0],norm(e.pos+e.time));
if (left!=-1&&dist(left,e.pos,e.time)<dist(left,oier_near[left].pos,e.time)){
q.erase(oier_near[left]);
comput_near[oier_near[left].pos][0]=0;
comput_near[e.pos][0]=left;
oier_near[left]={e.time+dist(left,e.pos,e.time),e.pos,0,2,0,left};
q.insert(oier_near[left]);
}
if (right!=-1&&dist(right,e.pos,e.time)<dist(right,oier_near[right].pos,e.time)){
q.erase(oier_near[right]);
comput_near[oier_near[right].pos][1]=0;
comput_near[e.pos][1]=right;
oier_near[right]={e.time+dist(right,e.pos,e.time),e.pos,0,2,0,right};
q.insert(oier_near[right]);
}
}
else if (e.type==1){
oier_dir[e.dir][norm((e.dir==1)? e.pos-e.time : e.pos+e.time)].insert(e.id);
link_oier(e.time,e.pos,e.dur,e.type,e.dir,e.id);
}
else{
e.dur=oier[e.id][3];
e.dir=oier[e.id][2];
ans[e.id]={e.pos,e.time};
cnt++;
int pp=norm((e.dir==1)? e.pos-e.time : e.pos+e.time);
oier_dir[e.dir][pp].erase(e.id);
if (oier_dir[e.dir][pp].empty()) oier_dir[e.dir].erase(pp);
int o1=comput_near[e.pos][e.dir];
pp=norm((e.dir==1)? oier[e.id][1]-oier[e.id][0] : oier[e.id][1]+oier[e.id][0]);
int o2=(e.dir==1)? find_left_id(oier_dir[1],pp) : find_right_id(oier_dir[0],pp);
comput_near[e.pos]={0,0};
comput.erase(e.pos);
q.insert({e.time+e.dur,e.pos,0,0,0,0});
if (o1){
q.erase(oier_near[o1]);
oier_near[o1]={0,-1,0,0,0,0};
link_oier(e.time,oier[o1][1],oier[o1][3],1,oier[o1][2],o1);
}
if (o2!=-1){
link_oier(e.time,oier[o2][1],oier[o2][3],1,oier[o2][2],o2);
}
}
}
for (int i=1;i<=n;i++){
cout<<ans[i].first<<' '<<ans[i].second<<'\n';
}
return 0;
}