「模拟赛」暑期集训CSP提高模拟2(7.19)
学长组题+预告:题会有点难
雀氏。。。
题目列表
A. 活动投票
B. 序列
C. Legacy
D. DP搬运工1
A.活动投票
题意:
衡中活动很多,人也很多,一次活动有 $n$ 个学生参与投票,现已知一名参赛选手票数超过半数,求其参赛号 $ai$(参赛号随机,$0≤ai≤21474836470≤ai≤2147483647)$ 。很简单吧,重点来了: 时限:0.5s 内存:2M
赛时分析:
这道题难点就在于内存的限制,赛时可能对于计算内存的方法不是很清楚,打了个 \(map数组\) 自己算了算明显会超限制,但不知道自己的计算方法对不对,并且也想不出来别的做法,觉得能拿个五十分吧,就没仔细想跳了。结果实际得分:\(0 pts\),样例是一点分不想让我拿啊。
正解:
记一个 \(now\) 表示现在的数,\(cnt\) 表示现在的数的个数,如果遇到一个新的数 \(x\) 有 \(x=now\),我让 \(cnt\)++,否则 \(cnt\)- -, 当 \(cnt<=0\) 时,\(now=x\), 即将记的数变为现在遇到的数 \(x\) 。
做法很显然,可以理解为将不同的数消掉,相同的数保留,因为答案的个数大于总个数的一半,那么答案消不完,则最后留下的就是答案。
code:
#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
scanf("%d", &n);
int now = 0, cnt = 0;
for(int i=1; i<=n; i++){
int x; scanf("%d", &x);
if(cnt == 0) {now = x, cnt++; continue;}
if(x == now) cnt++;
if(x != now) cnt--;
}
cout<<now;
return 0;
}
其他:
%%%新高一学长神奇做法:把所有元素 mod 几个较小质数(像 7 、13 等),把余数个数记录下,每个模数下最多的余数一定是答案所得,再用 \(CRT\) 就做完了。
B. 序列
C. Legacy
题意:
\(n\) 个点,\(q\) 条边,边有三种:
- 点 \(u\) 连点 \(v\),权值为 \(w\);
- 点 \(u\) 连向区间 \([l, r]\) 中的所有点,权值为 \(w\);
- 区间 \([l, r]\) 连向点 \(u\),权值为 \(w\)。
给定 \(s\) 为起始点,求所有点到起始点的最小距离,如果某点到不了起始点则输出 -1。
赛时分析:
题目也没给 \(r-l\) 的取值范围啊,这我也不知道会不会炸啊,虽然挺显然会炸的(没给范围说明 \(r-l\) 可以为最大值 \(n\)),直接打了个暴力,对于有区间的边,循环遍历区间中的点建边,得分:\(20 pts\),其实赛时想到了点线段树和 + \(Dij\) 的,没有继续想,可能因为时间很急(着急打完暴力去打 T2)。
正解:
线段树优化建图板子题,不过没学过。
对于区间的边,把边连在区间包含于所给区间的子树根节点上,然后把子树上的点连通,边权为 0,这样可以保证点可以与区间中的每个实际点连通。
具体实现我们需要建两棵树,第一颗维护树上节点向下(子节点)连边,第二颗相反,维护节点向上(父节点)连边,然后将两棵树相同的叶子节点相连保证两棵树联通。
code:
#include<bits/stdc++.h>
#define lson rt << 1
#define rson rt << 1 | 1
#define ll long long
#define mp make_pair
using namespace std;
const int N = 1e6 + 10;
int n, Q, s, v, wa, op, turn[N], ma;
int tot, head[N], to[N<<3], nxt[N<<3], w[N<<3];
const int D=N>>1;
inline void addedge(int x, int y, int z){
w[++tot] = z;
to[tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
namespace Segment_tree
{
inline void jian(int k,int l,int r){
if(l==r){
turn[l]=k;
return;
}
addedge(k,k<<1,0),addedge(k,k<<1|1,0);
addedge((k<<1)+D,k+D,0),addedge((k<<1|1)+D,k+D,0);
int mid=l+r>>1;
jian(k<<1,l,mid),jian(k<<1|1,mid+1,r);
}
inline void geng(int k,int l,int r,int L,int R){
if(L<=l&&r<=R){
if(op==2)addedge(turn[v],k,wa);
else addedge(k+D,turn[v],wa);
return;
}
int mid=l+r>>1;
if(L<=mid)geng(k<<1,l,mid,L,R);
if(R>mid)geng(k<<1|1,mid+1,r,L,R);
}
}
ll dis[N]; bool vis[N];
priority_queue<pair<ll, int> >q;
void Dijkstra(int a){
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[turn[a]] = 0;
q.push(make_pair(-dis[turn[a]], turn[a]));
while(q.size())
{
int x = q.top().second;
q.pop(); vis[x] = 1;
// cout<<x<<endl;
for(int i=head[x]; i; i=nxt[i]){
int y = to[i];
// cout<<y<<'K'<<endl;
if(dis[y] > dis[x] + w[i]){
dis[y] = dis[x] + w[i];
if(!vis[y]) q.push(make_pair(-dis[y], y));
}
}
}
}
int main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
// cout<<1<<endl;
scanf("%d%d%d", &n, &Q, &s);
Segment_tree::jian(1, 1, n);
// cout<<1<<endl;
for(int i=1;i<=n;++i)
addedge(turn[i],turn[i]+D,0),addedge(turn[i]+D,turn[i],0);
for(int i=1; i<=Q; i++){
int u, l, r;
scanf("%d", &op);
if(op == 1){
scanf("%d%d%d", &u, &v, &wa);
addedge(turn[u], turn[v], wa);
}
else{
scanf("%d%d%d%d", &v, &l, &r, &wa);
Segment_tree::geng(1, 1, n, l, r);
}
}
// cout<<1<<endl;
Dijkstra(s);
for(int i=1; i<=n; i++){
printf("%lld ", dis[turn[i]] == 4557430888798830399 ? -1 : dis[turn[i]]);
}
return 0;
}