AtCoder Beginner Contest 133

AtCoder Beginner Contest 133

Time:7/7(Sun) 20:00

WebsiteAtCoder BC-133

C

题目描述:

给定区间[l,r],请你求出f(i,j)=i*j%2019 (l<=i<j<=r)的最小值。

数据范围:

0<=l,r<=\(2*10^9\)

题解:

由于数据范围很大,一开始觉得暴力枚举是不可能的了,然后就找规律,以为和2019的什么性质有关。

后来突然发现,只要r-l>=2019,那么在这之中必定存在2019的倍数,答案就为0。反之,r-l<2019,直接暴力枚举就好了。haosaoa

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll l,r;
int main(){
	scanf("%lld %lld",&l,&r);
	if(r-l>=2019){puts("0");return 0;}
	ll ma=2019;
	for(ll i=l;i<=r;i++)
		for(ll j=i+1;j<=r;j++)
			if(i*j%2019<ma)ma=i*j%2019;
	printf("%lld",ma);
}

D

题目描述:

有n座山(n为奇数),编号1..n,构成一个环,每两座山之间有一个水坝,当下雨时,如果某座山收集到x的水(x为偶数),则这些水会平分地流入两旁的水坝。现在已知第i座水坝收集到的水为\(a_i\),请求出一开始每座山收集到的雨水量。题目保证对于每组给定的数据,存在且仅存在一组可行解。

数据范围:
Constraints

All values in input are integers.
3≤N≤\(10^5-1\)
N is an odd number.
0≤\(A_i\)\(10^9\)
The situation represented by input can occur when each of the mountains receives a non-negative even number of liters of rain.

样例:

输入样例

5
3 8 7 5 5

输出样例

2 4 12 2 8
题解:

根据题意建立模型,设原来每座大山收集到的雨量为\(x_i\)

易知$$\frac{x1}{2}+\frac{x2}{2}=A_1$$
将等式同乘以2,即预处理时,将\(A_i\)乘上2。

然后得到下面式子:这是n=5的情况

\[\left\{ \begin{array}{c} x1+x2=A1……①\\ x2+x3=A2……②\\ x3+x4=A3……③\\ x4+x5=A4……④\\ x5+x1=A5……⑤\\ \end{array} \right. \]

发现这很小学奥数题,所以试着消元,将全部式子累加除2,得到\(x_1+..x_n\),再将①+③得到,\(x_1+..x_{n-1}\),两式作差,得到\(x_n\),然后依次解出其他方程。

注意,这种方法只适用于n为奇数的情况。而题目给定的n必定为奇数,所以这种方法对于此题完全适用。Code 1是这种消元方法的代码。而Code 2不受n的奇偶性限制,更具扩展性。

//Code 1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll a[100009],b[100009],sum;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),a[i]*=2;
	for(int i=1;i<=n;i++)sum+=a[i];
	sum/=2;
	ll pre=0;
	for(int i=1;i<n;i+=2)pre+=a[i];
	ll tmp=sum-pre;
	b[n]=tmp,b[1]=a[n]-tmp;
	for(int i=2;i<=n-1;i++)b[i]=a[i-1]-b[i-1];
	for(int i=1;i<=n;i++)printf("%lld ",b[i]);
}

依然是由上面那个方程组推来,这次我们将n设为偶数4

\(x1+x2=A1\) \(\rightarrow\) \(x2=A1-x1\)

相应的:\(x3=A2-x2=A2-A1+x1\)

\(x4=A3-x3=A3-A2+A1-x1\)

\(x1=A4-x4=A4-A3+A2-A1+x1\)

发现最后一条式子又迭代回了\(x1\),变成了易解的一元一次方程,由此,模拟这个迭代的过程,设前面一坨A的式子为a,b为方程式右边\(x1\)的系数,然后不断迭代就可以求出\(x1\)了。

//Code 2
#include<bits/stdc++.h>
#define LL long long
#define M 1000005 
using namespace std;
const int P=1e9+7; 
int  A[M];
int main(){
	int n,m;
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%d",&A[i]),A[i]<<=1;
	LL a=A[1],b=-1;
	for(int i=2;i<n;i++){
		a=A[i]-a;
		b*=-1;
	}
	LL x1=(A[n]-a)/(b+1);
	printf("%lld",x1);
	for(int i=1;i<n;i++){
		x1=A[i]-x1;
		printf(" %lld",x1);
	}
	puts("");
	return 0;
}

E

题目描述:

给定一棵n个节点的树,由n-1条边构成,第i条边连接节点\(u_i\)\(v_i\)。现在一共有k中颜色的染料,请你对这棵树的每一个节点进行染色,并满足下述条件:

  • 对于树上的任意两个节点\(u,v\),若它们间的距离小于等于2,则这两个节点所染的颜色必须不同。

请你求出可行的染色方案数。 由于答案可能较大,请输出答案%1 000 000 007后的结果。

数据范围&输入输出格式:
Constraints

1≤N,K≤\(10^5\)
1≤\(a_i,b_i\)≤N
The given graph is a tree.

输入第一行两个整数n,k。接下来的n-1行每行读入两个整数u,v,表示节点u,v间存在一条边。

输出一行,包含一个整数,意义见题面。

样例:

输入:

5 4
1 2
1 3
1 4
4 5

输出:

48
题解:

看数据范围,显然正解应该是O(N)的。如果颜色是3种的话可以直接树形Dp,O(N)得到。然而有k种颜色——组合数?显然可以——详见xiaoC代码,但是会很麻烦。我们试着将每一个节点单独处理出来,即利用乘法原理,单独统计每个节点对答案的贡献。

观察一下样例,发现,对于任意节点x,假设它旁边什么节点都没有(显然这是不可能的),那它有k种染色方案,如果他有父亲节点(fa[x]!=0),则k要-1,如果他还有祖父节点(fa[fa[x]]!=0),则k要再-1。如果他有num个兄弟节点(包括x自己),则k要减去(num-1)。那么最后将每个节点的方案数都乘起来——这就是答案吗?,发现连样例都过不了,因为我们这样统计单个节点x的时候,是在他距离为2之内的节点的颜色状态都确定下的情况,这样乘起来并没有考虑颜色的排列情况,故对于每组“一排儿子“还要乘以\(num!\),即颜色的排列情况。

但是仔细分析,完全不用那样搞,我们可以以某个节点x的所有儿子为一个单位进行统计,并计算对答案的贡献,其实本质与上面那个方法是一模一样的,只是将一组儿子一起统计,思路会更为清晰。

设节点x的儿子数位son[x],统计详见下面的伪代码,注意,既然x有儿子,则必然存在fa[son[x]],虽然废话,所以下面统计时是从k-s-1开始的。

For(x:=1 to n){

s=0;

If(x存在fa[x])s++;

ans*=(k-s-1)*(k-s-2)*....(k-s-son[x])

}

完整代码如下,记得当ans<=0时要输出0,即不存在可行的方案——至于为什么会存在ans<=0的情况,由上面统计过程就可知道。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const ll mod=1000000007;
const int N=1e5+5;
int d[N],son[N],fa[N],n,k;
ll ans=1;
vector<int>e[N];
int dfs(int x,int f){
	fa[x]=f;
	for(int i=0;i<e[x].size();i++){
		int y=e[x][i];
		if(y==f)continue;
		son[x]++;
		dfs(y,x);
	}
}
int main(){
	n=read(),k=read();
	for(int i=1,u,v;i<n;i++){
		u=read(),v=read();
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++){
		int s=0;
		if(fa[i])s++;
		for(int j=1;j<=son[i];j++)ans=(ans*1ll*(k-j-s))%mod;
	}
	ans=ans*1ll*k;
	if(ans<=0)puts("0");
	else printf("%lld",ans%mod);
}

F

题目描述:

依然是一棵n个节点的树,n-1条边构成,第i条边连接节点\(a_i,b_i\),且这条边颜色为\(c_i\),长度为\(d_i\)。每条边的颜色c为1~(n-1)的一个整数,且不同的数字代表不同的颜色,相同的颜色必定由相同的数字表示( 翻译自原题,其实是废话)。

现在给定m个查询操作Q

  • Q x y u v,表示若将所有颜色为x的边的长度改成y,那么节点u,v间的距离为多少?注意,只是假设,并不影响实际长度,也不影响后面的询问。
数据范围&输入输出格式
Constraints

2≤N≤\(10^5\)

1≤Q≤\(10^5\)

1≤\(a_i,b_i\)≤N

1≤\(c_i\)≤N−1

1≤\(d_i\)\(10^4\)

1≤\(x_j\)≤N−1

1≤\(y_j\)\(10^4\)

1≤\(u_j\)<\(v_j\)≤N

  • The given graph is a tree.
  • All values in input are integers.

输入,第一行包括两个整数n,q分别表示节点个数,询问个数;
之后的n-1行每行四个整数,描述树上的一条边;
最后q行每行四个整数x,y,u,v表示一个询问。

输出,对于每一个询问打印答案,每个一行。

样例:

输入:

5 3
1 2 1 10
1 3 2 20
2 4 4 30
5 2 1 40
1 100 1 4
1 100 1 5
3 1000 3 4

输出:

130
200
60
题解:

查询树上两点距离——用LCA,但是每一次询问,距离都会改变,所以不能这样搞。看到n 1e5的数据范围,硬上修改肯定也不行。

关注到询问的一个特性——每次修改都是暂时的,不会影响到后面,这暗示我们每个询问都是独立的!独立的?——那就把每个询问放在树上,离线处理!

怎么放呢,联想普通的求两点间距离,即dis(u,v)=dis(u)+dis(v)-2*dis(lca),又发现这三个节点——u,v,lca,对于答案的贡献都是单独的!所以,我们可以把询问拆成三份,分别放在这三个节点上,记录{i(第几个询问),co(要改的颜色),d(要改成的长度),k(属性——为下面的树上差分做准备)}。然后离线处理每个询问。

如何处理?显然O(N)跑一遍整棵树,定义状态(x,f,D)——当前节点为x,父亲节点为f,从树根到这走了D长度。先处理这个节点上的询问,

\[ret[now.id]+=now.k*(D-dis[now.co]+cnt[now.co]*now.d); \]

其实上面这个式子就是在利用树上差分统计答案,这样就可以独立每个节点对询问的贡献了。完整代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
int fa[N][20],dep[N];
int dis[N],cnt[N],ret[N];
struct node{int u,co,l;};
vector<node>e[N];
struct ques{int id,co,d,k;};
vector<ques>q[N];
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x;
}
void pre(int x,int f){
	fa[x][0]=f,dep[x]=dep[f]+1;
	for(int i=0;i<e[x].size();i++)
		if(e[x][i].u!=f)pre(e[x][i].u,x);
}
int UP(int x,int step){
    for(int i=0;i<=17;i++)if(step&(1<<i))x=fa[x][i];
    return x;
}
int LCA(int a,int b){
    if(dep[a]>dep[b])swap(a,b);
    b=UP(b,dep[b]-dep[a]);
    if(a==b)return a;
    for(int i=17;i>=0;i--)
        if(fa[a][i]!=fa[b][i])a=fa[a][i],b=fa[b][i];
    return fa[a][0];
}
void dfs(int x,int f,int D){
	for(int i=0;i<q[x].size();i++){
		ques now=q[x][i];
		ret[now.id]+=now.k*(D-dis[now.co]+cnt[now.co]*now.d);
	}
	for(int i=0;i<e[x].size();i++){
		node y=e[x][i];
		if(y.u==f)continue;
		cnt[y.co]++,dis[y.co]+=y.l;
		dfs(y.u,x,D+(y.l));
		cnt[y.co]--,dis[y.co]-=y.l;
	}
}
int main(){
    n=read(),m=read();
    for(int i=1,a,b,c,d;i<n;i++){
        a=read(),b=read(),c=read(),d=read();
        e[a].push_back((node){b,c,d});
        e[b].push_back((node){a,c,d});
    }
    
	pre(1,0);
    for(int i=1;i<=17;i++)
        for(int j=1;j<=n;j++)fa[j][i]=fa[fa[j][i-1]][i-1];	
        
    for(int i=1,u,v,c,d;i<=m;i++){
       c=read(),d=read(),u=read(),v=read();
       q[u].push_back((ques){i,c,d,1});
       q[v].push_back((ques){i,c,d,1});
       q[LCA(u,v)].push_back((ques){i,c,d,-2});
    }
	dfs(1,0,0);
	
    for(int i=1;i<=m;i++)printf("%d\n",ret[i]);
} 
posted @ 2019-07-14 21:52  real_lyb  阅读(345)  评论(0编辑  收藏  举报