day1:
T1:
题目:luogu5019.
题目大意:给定一个长度为 n n n的数组 A [ i ] A[i] A[i],要求每次可以将一段连续正数区间减 1 1 1,要求用最少的操作次数将 A [ i ] A[i] A[i]全部变为 0 0 0.
1 ≤ n ≤ 1 0 5 , 1 ≤ A [ i ] ≤ 1 0 4 1\leq n\leq 10^5,1\leq A[i]\leq 10^4 1≤n≤105,1≤A[i]≤104.
这貌似是NOIP原题,显然当一个数比它前面的数小的时候,这个数不会有贡献,否则这个数会产生与前面的数的差的贡献.
所以我们只需要枚举一下 i i i就可以了,时间复杂度 O ( n ) O(n) O(n).
代码如下:
#include
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
int a[N+9],n;
LL sum;
Abigail into(){
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
}
Abigail work(){
for (int i=1;i<=n;++i)
if (a[i]>a[i-1]) sum+=a[i]-a[i-1];
}
Abigail outo(){
printf("%lld\n",sum);
}
int main(){
into();
work();
outo();
return 0;
}
T2:
题目:luogu5020.
题目大意:给定一个系统 ( n , a ) (n,a) (n,a)表示数组 a a a有 n n n个数,现在定义两个系统 ( n , a ) (n,a) (n,a)和 ( m , b ) (m,b) (m,b)相同是指这个数组 a a a的数能够组成的所有数 b b b也能组成,而 a a a不能组成的数 b b b也不能组成.现在给定一个系统 ( n , a ) (n,a) (n,a),要求 m m m最小的系统 ( b , m ) (b,m) (b,m)满足 ( b , m ) (b,m) (b,m)与 ( a , n ) (a,n) (a,n)相同.
1 ≤ n ≤ 100 , 1 ≤ a [ i ] ≤ 2.5 ∗ 1 0 4 1\leq n\leq 100,1\leq a[i]\leq 2.5*10^4 1≤n≤100,1≤a[i]≤2.5∗104.
考场先是想到ex_gcd,然后突然发现解不能是负数.
考场上想到应该就是要将在 ( n , a ) (n,a) (n,a)所有能用 a a a数组内其他数组成的数给去掉,想了很久也没有反例.
然后就想到用一个背包来完成这个事情,时间复杂度 O ( n max { a [ i ] } ) O(n\max\left \{ a[i] \right \}) O(nmax{a[i]}),可过.
代码如下:
#include
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100,M=25000;
int n,ma,a[N+9];
int f[M+9],ans;
void add(int &x,int y){
x+=y;
if (x>2) x=2;
}
Abigail start(){
for (int i=1;i<=ma;++i)
f[i]=0;
ans=0;
}
Abigail into(){
scanf("%d",&n);
for (int i=1;i<=n;++i){
scanf("%d",&a[i]);
ma=max(ma,a[i]);
f[a[i]]=1;
}
}
Abigail work(){
n=0;
for (int i=1;i<=ma;++i)
if (f[i]) a[++n]=i;
for (int i=1;i<=n;++i)
for (int j=a[i];j<=ma;++j)
add(f[j],f[j-a[i]]);
}
Abigail outo(){
for (int i=1;i<=n;++i)
if (f[a[i]]==1) ++ans;
printf("%d\n",ans);
}
int main(){
int T=0;
scanf("%d",&T);
while (T--){
start();
into();
work();
outo();
}
return 0;
}
T3:
题目:luogu5021.
题目大意:给定一棵无根树,现在让你找 m m m条没有边重叠的路径,使得这些路径中边权和最小的路径边权和最大.
1 ≤ n ≤ 5 ∗ 1 0 4 1\leq n\leq 5*10^4 1≤n≤5∗104.
考场上想到二分,但因为这是最后一题,认为自己是做不出来的就写了55分的部分分,现在想想这道题就是道水题啊.
首先二分最小的路径和 m i d mid mid,将问题转化为判定是否有 m m m条不重叠的路径每条的边权和都大于或等于 m i d mid mid.
然后考虑菊花图的部分分的做法,我们可以发现就是将所有边按照边权排序,最大值与最小值配对就可以了,时间复杂度 O ( n log n ) O(n\log n) O(nlogn).
再来考虑分叉不超过 3 3 3的做法,我们肯定能将树转化成一棵二叉树.考虑在这棵二叉树上解决这个问题,考虑记录一个 f [ i ] f[i] f[i]表示点i字数中剩下的(也就是子树中所有满足条件的链抽走后)以 i i i为一端的最长链.考虑计算每一棵以k为根的子树中最多可以抽走的链 c n t [ k ] cnt[k] cnt[k],发现 c n t [ k ] cnt[k] cnt[k]是 k k k的所有儿子的 c n t cnt cnt值之和,加上 0 0 0~ 2 2 2条链,加上的链的数量是很好求的, f [ k ] f[k] f[k]也可以很容易的求出.
对于一般情况,考虑将上面两个算法结合起来.在每一棵子树计算最多可以抽走的链时,按照菊花图的方式求出,这样你就能够得到80分的高分了,剩下20分WA了.
为什么会WA呢?考虑对于若每一个节点 k k k的所有儿子的值计算出来都是正确的,那么求 c n t [ k ] cnt[k] cnt[k]的时候按照菊花图的方式是正确的,但是 f [ k ] f[k] f[k]的计算就会出错了.因为我们发现, c n t [ k ] cnt[k] cnt[k]按照菊花图的方式求出一定最优,但是当 c n t [ k ] cnt[k] cnt[k]最优时 f [ k ] f[k] f[k]会有多种情况,而菊花图的处理方式是会使 f [ k ] f[k] f[k]最差而不是最好的方式.
那么如何处理这个问题呢?考虑可以二分,也可以考虑直接使用multiset来解决这个问题.用multiset的时候,考虑从大往小枚举,每次抽走最小的与当前枚举到的值满足条件的值,就可以保证是最好的方式了.时间复杂度 O ( n log n log ∑ l i ) O(n\log n\log\sum l_i) O(nlognlog∑li).
代码如下:
#include
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=50000;
struct side{
int y,next,v;
}e[N*2+9];
int lin[N+9],n,m,top,ans;
void ins(int x,int y,int v){
e[++top].y=y;e[top].v=v;
e[top].next=lin[x];
lin[x]=top;
}
int cnt[N+9],f[N+9];
int tmp[N+9],tt,u[N+9];
multiset<int> s;
void calc(int k,int fa,int mid){
s.clear();
for (int i=lin[k];i;i=e[i].next)
if (e[i].y^fa) s.insert(e[i].v+f[e[i].y]);
multiset<int>::iterator l,r;
multiset<int>::reverse_iterator ii;
while (!s.empty()){
ii=s.rbegin();
if (*ii>=mid) s.erase(s.find(*ii)),++cnt[k];
else break;
}
while (!s.empty()){
l=s.begin();
s.erase(l);
r=s.lower_bound(mid-*l);
if (r==s.end()) f[k]=*l;
else s.erase(r),++cnt[k];
}
}
void dfs(int k,int fa,int mid){
for (int i=lin[k];i;i=e[i].next)
if (e[i].y^fa){
dfs(e[i].y,k,mid);
cnt[k]+=cnt[e[i].y];
}
calc(k,fa,mid);
}
bool check(int mid){
for (int i=1;i<=n;++i)
cnt[i]=f[i]=0;
dfs(1,0,mid);
return cnt[1]>=m;
}
Abigail into(){
scanf("%d%d",&n,&m);
int x,y,v;
for (int i=1;i<n;++i){
scanf("%d%d%d",&x,&y,&v);
ins(x,y,v);ins(y,x,v);
}
}
Abigail work(){
for (int i=29;i>=0;--i)
if (check(ans+(1<<i))) ans+=1<<i;
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}
day2:
T1:
题目:luogu5022.
题目大意:给定一张图有 n n n个点 m m m条边,求它字典序最小的dfs序.
1 ≤ n ≤ 5000 , n − 1 ≤ m ≤ n 1\leq n\leq 5000,n-1\leq m\leq n 1≤n≤5000,n−1≤m≤n.
考场直接每个点建一个堆以为常数小机子快就能过…然后只拿了88分…
对于树的数据,直接在每一个点建一个堆,在深度优先遍历的时候先遍历编号最小的点即可,时间复杂度 O ( n log n ) O(n\log n) O(nlogn).
对于基环树,在树的基础上,暴力枚举删除一条边,将所有得到的字典序中最小的字典序当做答案,时间复杂度 O ( n 2 log n ) O(n^2\log n) O(n2logn),然后你就可以拿到88分的高分.
然后考场下来的时候,听到zjc说 O ( n 2 log n ) O(n^2\log n) O(n2logn)会被卡,直接把每一个点的出边排个序就可以做到 O ( n 2 ) O(n^2) O(n2)了,但是我写出来的代码还是优秀的88分,看起来是被卡常了…
我们再考虑一个更优秀的做法,对原算法进行常数优化,考虑使用邻接表来储存,直接排完序逆向加边.
AC代码如下:
#include
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=5000;
struct side{
int y,next;
}e[N*2+9];
int lin[N+9],top,n,m;
void ins(int x,int y){
e[++top].y=y;
e[top].next=lin[x];
lin[x]=top;
}
struct sside{
int x,y;
bool operator < (const sside &p)const{return x<p.x||x==p.x&&y<p.y;}
}s[N*2+9];
int ts;
int ord[N+9],tmp[N+9],tt;
int nx,ny,u[N+9];
int xx[N+9],yy[N+9];
void dfs(int k){
u[k]=1;
tmp[++tt]=k;
for (int i=lin[k];i;i=e[i].next)
if (!u[e[i].y]&&(e[i].y^nx||k^ny)&&(e[i].y^ny||k^nx)) dfs(e[i].y);
}
Abigail into(){
scanf("%d%d",&n,&m);
int x,y;
for (int i=1;i<=m;++i){
scanf("%d%d",&x,&y);
xx[i]=x;yy[i]=y;
s[++ts].x=x;s[ts].y=y;
s[++ts].y=x;s[ts].x=y;
}
}
Abigail work(){
sort(s+1,s+1+ts);
for (int i=ts;i>=1;--i)
ins(s[i].x,s[i].y);
for (int i=1;i<=n;++i) ord[i]=n;
if (n==m+1){
dfs(1);
for (int i=1;i<=n;++i)
ord[i]=tmp[i];
return;
}
int flag;
for (int i=1;i<=m;++i){
tt=0;
nx=xx[i],ny=yy[i];
for (int i=1;i<=n;++i) u[i]=0;
dfs(1);
if (tt<n) continue;
flag=0;
for (int i=1;i<=n;++i)
if (tmp[i]^ord[i]){
flag=tmp[i]<ord[i];
break;
}
if (!flag) continue;
for (int i=1;i<=n;++i)
ord[i]=tmp[i];
}
}
Abigail outo(){
for (int i=1;i<n;++i)
printf("%d ",ord[i]);
printf("%d\n",ord[n]);
}
int main(){
into();
work();
outo();
return 0;
}
T2:
题目:luogu5023.
题目大意:大概就是让你求有多少个 n n n行 m m m列的 01 01 01矩阵,使得对于任意两条路径 p 1 p1 p1和 p 2 p2 p2,若 w ( p 1 ) > w ( p 2 ) w(p1)>w(p2) w(p1)>w(p2),则 s ( p 1 ) ≤ s ( p 2 ) s(p1)\leq s(p2) s(p1)≤s(p2).其中函数 w ( p ) w(p) w(p)表示这个路径的行走方向依次排列, 1 1 1为向右, 0 0 0为向下; s ( p ) s(p) s(p)表示路径行走到的格子内的数依次排列.
1 ≤ n ≤ 8 , 1 ≤ m ≤ 1 0 6 1\leq n\leq 8,1\leq m\leq 10^6 1≤n≤8,1≤m≤106.
这道题其实打表打完之后瞪眼法找规律就可以了(虽然我考场表都没打,以为是个状压).
下面给出规律:
我们设 ( a , b ) (a,b) (a,b)表示 a = n , b = m a=n,b=m a=n,b=m时的答案.
( 1 , m ) = 2 m (1,m)=2^m (1,m)=2m
( n , m ) = ( m , n ) (n,m)=(m,n) (n,m)=(m,n)
( 2 , 2 ) = 12 , ( 3 , 3 ) = 112 , ( 4 , 4 ) = 912 (2,2)=12,(3,3)=112,(4,4)=912 (2,2)=12,(3,3)=112,(4,4)=912
若 m = n + 1 m=n+1 m=n+1且 n ≤ 3 n\leq 3 n≤3,则 ( n , m ) = 3 ( n , m − 1 ) (n,m)=3(n,m-1) (n,m)=3(n,m−1).若 m = n + 1 m=n+1 m=n+1且 n > 3 n>3 n>3,则 ( n , m ) = 3 ( n , m − 1 ) − 3 n (n,m)=3(n,m-1)-3^n (n,m)=3(n,m−1)−3n.
若 m > n + 1 m>n+1 m>n+1,则 ( n , m ) = 3 ( n , m − 1 ) (n,m)=3(n,m-1) (n,m)=3(n,m−1).
那么代码如下:
#include
using namespace std;
#define Abigail inline void
typedef long long LL;
const LL mod=1000000007;
int n,m;
LL ans;
LL power(LL a,int k){
LL s=1;
for (;k;k>>=1,a=a*a%mod)
if (k&1) s=s*a%mod;
return s;
}
Abigail into(){
scanf("%d%d",&n,&m);
}
Abigail work(){
if (n>m) swap(n,m);
switch (n){
case 1:
ans=power(2,m);
return;
case 2:
ans=12;
break;
case 3:
ans=112;
break;
case 4:
ans=912;
break;
default:
ans=912;
for (int i=5;i<=n;++i)
ans=ans*8-(5<<i);
}
if (m>=n+1) ans=n>3?ans*3-(3<<n):ans*3;
if (m>n+1) ans=ans*power(3,m-n-1)%mod;
}
Abigail outo(){
printf("%lld\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}
T3:
题目:luogu5024.
题目大意:给定一棵树,要求对树染色,且一条边上的两点必须有一点染色,现在给定 m m m个询问,每个询问强制两点的染色状态,求最小染色点权和.
这道题让我在考场以为可以用线段树来维护链的情况,然后直接用树链剖分维护树上的情况就行了,然而这样并不好处理.
看完题解后突然感觉很容易用倍增预处理来处理这些问题啊.
首先我们预处理出一个f数组,其中 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示在以点i为根的子树不选 / / /选根时的最小点权和.
然后考虑确定了两个点 u u u和 v v v的染色状态时,会改变的链其实是 u u u到 L C A LCA LCA, v v v到 L C A LCA LCA和 L C A LCA LCA到根这三条链.
那么我们先不考虑这三条链之外的树节点,我们发现只要用倍增预处理 f [ u ] [ i ] [ 0 / 1 ] [ 0 / 1 ] f[u][i][0/1][0/1] f[u][i][0/1][0/1]表示点 u u u到 u u u的第 2 i 2^i 2i级祖先时, u u u的状态为不选/选, u u u的 2 i 2^i 2i级祖先的状态为选 / / /不选,且不包括子树 u u u( u u u的点权也不包括)时,这条链的最小染色点权和,预处理时空复杂度 O ( n log n ) O(n\log n) O(nlogn).
然后,我们发现还有其它的树节点的影响,但是这样并不影响,我们只需要把状态 f [ u ] [ i ] [ 0 / 1 ] [ 0 / 1 ] f[u][i][0/1][0/1] f[u][i][0/1][0/1]表示的链上其它连接的子树的值顺便加上就可以了,时空复杂度依然是 O ( n log n ) O(n\log n) O(nlogn).
代码中 f f f数组的预处理什么的,可能细节比较多,注意一点就可以了.
代码如下:
#include
using namespace std;
#define Abigail inline void
typedef long long LL;
int ri(){
int x=0;
char c;
for (c=getchar();c<'0'||c>'9';c=getchar());
for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
return x;
}
const int N=100000,C=19;
const LL INF=(1LL<<50)-1LL;
int n,m;
LL p[N+9];
struct side{
int y,next;
}e[N*2+9];
int lin[N+9],top;
void ins(int x,int y){
e[++top].y=y;
e[top].next=lin[x];
lin[x]=top;
}
LL dp[N+9][2];
void dfs_dp(int k,int fa){
dp[k][0]=0;dp[k][1]=p[k];
for (int i=lin[k];i;i=e[i].next)
if (e[i].y^fa){
dfs_dp(e[i].y,k);
dp[k][0]+=dp[e[i].y][1];
dp[k][1]+=min(dp[e[i].y][0],dp[e[i].y][1]);
}
}
int gr[N+9][C+1],deep[N+9];
LL f[N+9][C+1][2][2];
void dfs_lca(int k,int fa){
deep[k]=deep[fa]+1;
gr[k][0]=fa;
f[k][0][0][0]=INF;
f[k][0][0][1]=f[k][0][1][1]=dp[fa][1]-min(dp[k][0],dp[k][1]);
f[k][0][1][0]=dp[fa][0]-dp[k][1];
int F;
for (int i=1;i<=C;++i){
F=gr[k][i-1];
gr[k][i]=gr[F][i-1];
f[k][i][0][0]=min(f[k][i-1][0][0]+f[F][i-1][0][0],f[k][i-1][0][1]+f[F][i-1][1][0]);
f[k][i][0][1]=min(f[k][i-1][0][0]+f[F][i-1][0][1],f[k][i-1][0][1]+f[F][i-1][1][1]);
f[k][i][1][0]=min(f[k][i-1][1][0]+f[F][i-1][0][0],f[k][i-1][1][1]+f[F][i-1][1][0]);
f[k][i][1][1]=min(f[k][i-1][1][0]+f[F][i-1][0][1],f[k][i-1][1][1]+f[F][i-1][1][1]);
}
for (int i=lin[k];i;i=e[i].next)
if (e[i].y^fa) dfs_lca(e[i].y,k);
}
LL get_ans(int u,int a,int v,int b){
if (deep[u]<deep[v]) swap(u,v),swap(a,b);
LL u0,u1,v0,v1,l0,l1,t0,t1,ans;
int l;
u0=u1=v0=v1=l0=l1=INF;
a?u1=dp[u][1]:u0=dp[u][0];b?v1=dp[v][1]:v0=dp[v][0];
for (int i=C;i>=0;--i)
if (deep[gr[u][i]]>=deep[v]){
t0=u0;t1=u1;
u0=min(t0+f[u][i][0][0],t1+f[u][i][1][0]);
u1=min(t0+f[u][i][0][1],t1+f[u][i][1][1]);
u=gr[u][i];
}
if (u^v){
for (int i=C;i>=0;--i)
if (gr[u][i]^gr[v][i]){
t0=u0;t1=u1;
u0=min(t0+f[u][i][0][0],t1+f[u][i][1][0]);
u1=min(t0+f[u][i][0][1],t1+f[u][i][1][1]);
t0=v0;t1=v1;
v0=min(t0+f[v][i][0][0],t1+f[v][i][1][0]);
v1=min(t0+f[v][i][0][1],t1+f[v][i][1][1]);
u=gr[u][i];
v=gr[v][i];
}
l=gr[u][0];
l0=u1+v1+dp[l][0]-dp[u][1]-dp[v][1];
l1=min(u0,u1)+min(v0,v1)+dp[l][1]-min(dp[u][0],dp[u][1])-min(dp[v][0],dp[v][1]);
}else{
l=u;
b?l1=u1:l0=u0;
}
if (l==1){
ans=min(l0,l1);
return ans<INF?ans:-1;
}
for (int i=C;i>=0;--i)
if (gr[l][i]>1){
t0=l0;t1=l1;
l0=min(t0+f[l][i][0][0],t1+f[l][i][1][0]);
l1=min(t0+f[l][i][0][1],t1+f[l][i][1][1]);
l=gr[l][i];
}
ans=min(dp[1][0]-dp[l][1]+l1,dp[1][1]-min(dp[l][0],dp[l][1])+min(l1,l0));
return ans<INF?ans:-1LL;
}
Abigail into(){
n=ri();m=ri();
int x=ri(),y;
for (int i=1;i<=n;++i) p[i]=LL(ri());
for (int i=1;i<n;++i){
x=ri();y=ri();
ins(x,y);ins(y,x);
}
}
Abigail work(){
dfs_dp(1,0);
dfs_lca(1,0);
}
Abigail getans(){
int u,a,v,b;
for (int i=1;i<=m;++i){
u=ri();a=ri();v=ri();b=ri();
printf("%lld\n",get_ans(u,a,v,b));
}
}
int main(){
into();
work();
getans();
return 0;
}