CSP-S模拟2复盘

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值