AtCoder Beginner Contest 133
Time:7/7(Sun) 20:00
Website: AtCoder 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]);
}
来源:oschina
链接:https://my.oschina.net/u/4261678/blog/3464977