极其良(e)心的一次考试
1.管道(pipe)
Description
给你一个城市下水道网络图,你需要选出一些管道,使得在只使用这些管道的情况下,令整个网络联通,并且花费最小
网络图可以看做是无向连通图,有n个节点和m条边,每条边连接
ui
u
i
和
vi
v
i
,选择的花费是
wi
w
i
不巧的是,由于某些原因,现在市政局要求选定某条特定的边管道,你的任务是求出对于某一条边,在选择这条管道的前提下的最小花费
Input
第1行包含两个整数n,m,表示点数和边数
第2~m+1行每行三个整数
ui
u
i
,
vi
v
i
,
wi
w
i
,表示有一条管道连接
ui
u
i
和
vi
v
i
,费用为
wi
w
i
Output
输出m行,每行一个整数,表示选择第i条管道的前提下的最小花费
管道按输入的顺序编号为1~m
Sample Input
5 7
1 2 3
1 3 1
1 4 5
2 3 2
2 5 3
3 4 2
4 5 4
Sample Output
9
8
11
8
8
8
9
Hint
对于20%的数据,n<=1000,m<=2000
对于另外20%的数据,m<=n+10
对于100%的数据,2<=n<=100000,1<=m<=200000
保证初始图连通
棋盘(chessboard)
Description
给你一个棋盘,上面有n*n个格子,一开始全部被涂上了黑色。你有一把魔术刷,可以将其中一些格子涂成白色。可惜的是,你不懂事的小妹妹把棋盘摔到了地上,导致其中一些格子摔坏了,不能被涂上白色
我们定义一个染色方案的魔法值是它最大白色子正方形的边长,你的任务是计算出对于每一个魔法值,有多少种不同的染色方案可以得到它
例如,N=3时,棋盘如J(a)所示,有两种染色方案可以得到2的魔法值,分别如J(b)和J(c)所示
由于方案数可能很大,你只需要输出它对10^9+7取模后的值
Input
第1行包含一个正整数n,表示棋盘的边长
第2~n+1行,每行包含一个长度为n的字符串,只含有“o”和“*”,“o”表示一个完好的格子,“*”表示一个摔坏的格子
Output
输出n+1行,每行包含一个整数,表示魔法值为i-1的方案数模10^9+7后的值
Sample Input 1
3
oo*
ooo
***
Sample Output 1
1
29
2
0
Sample Input 2
8
oooooooo
oooooooo
oooooooo
oooooooo
oooooooo
oooooooo
oooooooo
oooooooo
Sample Output 2
1
401415247
525424814
78647876
661184312
550223786
365317939
130046
1
Hint
对于10%的数据,n<=3
对于20%的数据,n<=4
对于30%的数据,n<=5
对于50%的数据,n<=6
对于70%的数据,n<=7
对于100%的数据,n<=8
3.颜色(color)
Description
世界上有n种颜色,每种颜色有着一个美丽度
现在你有一根分成n段的木棒,分别被涂上了颜色,可以将木棒看做长度为n的颜色序列。一段木棒的美丽度定义为出现的颜色的美丽度之和,如果一种颜色出现了多次,也只被计算一次
现在你需要回答一些询问,每个询问形如
(li,ri)
(
l
i
,
r
i
)
,意思是询问木棒上
[li,ri]
[
l
i
,
r
i
]
这一段的美丽度
由于世界线收束,有时候某段的颜色会被修改
Input
第1行包含两个整数n和m,表示木棒的长度和操作个数
第2行包含n个整数,表示每一段的初始颜色ci
第3行包含n个整树,表示每种颜色的美丽度wi
第4~m+3行,每行第1个整数kind
若kind=1,后面跟两个整数p和c,表示将第p段的颜色改成c
若kind=2,后面跟两个整数l和r,表示询问[li,ri]的美丽度
Output
对于每个kind=2的操作,输出一行一个整数,表示
[li,ri]
[
l
i
,
r
i
]
的美丽度
Sample Input
3 3
1 2 3
1 2 3
2 1 3
1 1 2
2 1 3
Sample Output
6
5
Hint
对于10%的数据,n,m<=1000
对于另外10%的数据,保证所有2操作在1操作之后
对于另外20%的数据,保证wi=1
对于另外10%的数据,n,m<=30000
对于另外10%的数据,n,m<=50000
对于另外10%的数据,n,m<=70000
对于另外10%的数据,n,m<=80000
对于另外10%的数据,n,m<=90000
对于100%的数据,n,m<=100000
策略:
T1:看起来像是一个最小生成树?内含一个LCA?很可做
预计分:100
实际得分:100
T2:数学?搜索?算了看都不想看
预计分:0
实际得分:你懂的
T3:树状数组么?没学啊(吐血~),看看能不能线段树来一波,算了太难了还是暴力吧
预计分:10
实际得分:30
T1:
给你n个点,m条边,包含第i条边的最小生成树的大小
可以证明,包含第i条边的最小生成树的其他边一定是属于原最小生成树的
那么第i条边连接的
ui
u
i
和
vi
v
i
的路径上的边都是可以删去的(当然选最大的边删去),树上路径当然LCA(或树剖),我们在倍增求LCA的时候维护一下最大边即可。
#include <bits/stdc++.h>
using namespace std;
struct Edge {
int u;
int v;
int c;
int point;
}edge[400010];
int fa[100010],num[100010],n,m;
int head[100010],to[200010],c[200010],net[200010],tot=0;
int f[100010][20],cost[100010][20],maxdeep=0,top,deep[100010];
long long sum=0;
bool use[200010];
int read() {
int ans=0,flag=1;
char ch=getchar();
while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
if(ch=='-') flag=-1,ch=getchar();
while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
return ans*flag;
}
bool cmp(Edge a,Edge b) {return a.c<b.c;}
int find(int x) {
if(x!=fa[x])
fa[x]=find(fa[x]);
return fa[x];
}
void merge(int fx,int fy) {
if(num[fx]>num[fy]) swap(fx,fy);
fa[fx]=fy;
num[fy]+=num[fx];
num[fx]=0;
return ;
}
void addedge(int u,int v,int w) {
to[++tot]=v;
c[tot]=w;
net[tot]=head[u];
head[u]=tot;
return ;
}
void work() {
int fx,fy;
for(int i=1;i<=n;i++) fa[i]=i,num[i]=1;
for(int i=m+1;i<=m*2;i++) {
fx=find(edge[i].u);
fy=find(edge[i].v);
if(fx!=fy) {
merge(fx,fy);
use[edge[i].point]=1;
addedge(edge[i].u,edge[i].v,edge[i].c);
addedge(edge[i].v,edge[i].u,edge[i].c);
sum+=edge[i].c;
}
}
return ;
}
void build(int x) {
for(int i=head[x];i;i=net[i]) {
if(to[i]==f[x][0]) continue;
f[to[i]][0]=x;
cost[to[i]][0]=c[i];
deep[to[i]]=deep[x]+1;
maxdeep=max(maxdeep,deep[to[i]]);
build(to[i]);
}
return ;
}
void prepare() {
for(top=0;maxdeep>(1<<top);top++);
for(int k=1;k<=top;k++) {
for(int i=1;i<=n;i++) {
cost[i][k]=max(cost[i][k-1],cost[f[i][k-1]][k-1]);
f[i][k]=f[f[i][k-1]][k-1];
}
}
return ;
}
int lca(int x,int y) {
int maxans=0;
if(deep[x]<deep[y])
swap(x,y);
for(int i=top;i>=0;i--)
if(deep[f[x][i]]>=deep[y]) {
maxans=max(maxans,cost[x][i]);
x=f[x][i];
}
if(x==y) return maxans;
for(int i=top;i>=0;i--) {
if(f[x][i]!=f[y][i]) {
maxans=max(maxans,cost[x][i]);
maxans=max(maxans,cost[y][i]);
x=f[x][i];
y=f[y][i];
}
}
maxans=max(maxans,cost[x][0]);
maxans=max(maxans,cost[y][0]);
return maxans;
}
int main() {
freopen("pipe.in","r",stdin);
freopen("pipe.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=m;i++) {
edge[i].u=read();
edge[i].v=read();
edge[i].c=read();
edge[i+m].u=edge[i].u;
edge[i+m].v=edge[i].v;
edge[i+m].c=edge[i].c;
edge[i].point=edge[i+m].point=i;
}
sort(edge+1+m,edge+m+1+m,cmp);
work();
deep[1]=1;
build(1);
prepare();
for(int i=1;i<=m;i++) {
if(use[i]) printf("%lld\n",sum);
else {
printf("%lld\n",sum-(long long)lca(edge[i].u,edge[i].v)+edge[i].c);
}
}
return 0;
}
T2:
听说这题是动态规划?我居然信了…
暂时还没有看懂QAQ
T3:
这是这套试题中最玄学的一题
原本10分的暴力莫名变成了30分?
打了莫队+暴力还没有纯暴力分高?
好吧,我服
题解没有看懂,但是发现了一个神奇的算法——莫队(还没看懂)
又发现了一个神奇的算法——分块
可以解决这个题目部分分(
wi
w
i
=1)
用pre[i]记录前一个和i相同颜色的段的所在位置
询问l到r时,如果pre[i]不在区间[l,r]内(即
pre[i]<l
p
r
e
[
i
]
<
l
)说明在l到i这一段没用和i颜色相同的段,则ans++
每一块内按pre[i]排序,然后二分查找即可,ans=find(l,r)-l
总结
这次考试状况连连,话说找不到机子考试是什么鬼~键盘比较反人类,打起来黏黏的,不舒服。大家考的好像都不怎么样(abs请无视),但是自我感觉还是可以的,心态没有爆炸,考出了自己的水平(就是一个字——稳)。加油吧,向NOIP冲刺!