今天发挥也很差,失误很多,效率很低。反思,改正!
上午比赛时很快过了前两题,但是后来精力不够集中。
想出F题n^5做法然后想前缀和优化是错的。本可以直接骗数据+打表,但是不熟悉Assert用法,提交尝试非常无效,导致浪费时间,最后也没过。不应该盲目提交,一定要多动脑子,跳出思维定式。不要用空间计算具体数字。用Assert二分出范围就好。大胆尝试,不要觉得有个细节不行就不去尝试。
今天C题感觉贪心有问题,但最后竟然是正解(到现在我还是觉得有问题,明天再去问)但总之数据不强,大胆尝试很重要
注意默认提交为C,要记得改成C++
不要浮躁去做新题和打其他比赛,比如今天打atcoder又留了好多坑,一定要优先把当天的题调完。今天的题没调太不应该了。
无论是写什么题想清楚再写,绝不要想着边写边调真的非常浪费时间。
吃饭和回寝室必须抓紧时间,回寝室立即洗漱,包括发消息总共最多0.5h
应在11点开始写总结,回顾今天的题和状态,早点睡觉
A:树上求加权中位数:链剖+二分,数据结构好题。
中位数与边权无关,只要距离和最小就行,用lct写了一发,不如链剖好写,因为链剖其实就只需要二分一个中位数,其他都很好维护。注意lct时只要遍历splay就要pushdown,比如二分时要pushdown。而且lct不好求lca(因为根固定,要先access再打标记,于是强行写了个倍增lca,好蠢,巨慢!)注意求lca是dth是不能带边权的。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 150020
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
typedef long long ll;
struct node{
int next,to,w;
}e[maxn * 2];
struct node2{
ll sum,mul;
};
int head[maxn],cnt;
int w[maxn],jump[20][maxn],fa[maxn];
int n,T,q,dth[maxn];
ll d[maxn];
struct LCT{
ll sum[maxn],mul[maxn];
int fa[maxn],ch[maxn][2],rev[maxn];
int stack_[maxn],tops;
void print(int x){
cout<<x<<" "<<ch[x][0]<<" "<<ch[x][1]<<endl;
if ( ls(x) ) print(ls(x));
if ( rs(x) ) print(rs(x));
}
void clear(){
for (int i = 1 ; i <= n ; i++) fa[i] = rev[i] = ch[i][0] = ch[i][1] = 0 , sum[i] = mul[i] = 0;
tops = 0;
}
inline void update(int x){
sum[x] = sum[ls(x)] + sum[rs(x)] + w[x];
mul[x] = mul[ls(x)] + mul[rs(x)] + (ll)w[x] * d[x];
}
inline void reverse(int x){
if ( !x ) return;
swap(ls(x),rs(x));
rev[x] ^= 1;
}
inline void pushdown(int x){
if ( rev[x] ){
reverse(ls(x));
reverse(rs(x));
rev[x] = 0;
}
}
inline bool isroot(int x){
return (ls(fa[x]) != x) && (rs(fa[x]) != x);
}
inline void rotate(int x){
int y = fa[x] , t = rs(y) == x , z = ch[x][1 - t];
fa[x] = fa[y];
if ( !isroot(y) ) ch[fa[y]][rs(fa[y]) == y] = x;
fa[y] = x , ch[x][1 - t] = y , ch[y][t] = z;
if ( z ) fa[z] = y;
update(y);
}
inline void splay(int x){
tops = 0 , stack_[++tops] = x;
for (int i = x ; !isroot(i) ; i = fa[i]) stack_[++tops] = fa[i];
while ( tops ) pushdown(stack_[tops--]);
while ( !isroot(x) ){
int y = fa[x] , z = fa[y];
if ( !isroot(y) && !((ls(y) == x) ^ (ls(z) == y)) ) rotate(y);
else rotate(x);
if ( !isroot(x) ) rotate(x);
}
update(x);
}
void access(int x){
for (int t = 0 ; x ; t = x,x = fa[x]){
splay(x) , ch[x][1] = t , update(x);
}
}
inline void makeroot(int x){
access(x) , splay(x) , reverse(x);
}
inline void link(int x,int y){
makeroot(x) , splay(x) , access(y) , splay(y);
fa[x] = y , ch[y][1] = x , update(y);
}
int query_mid(int x,int y){
makeroot(x) , access(y);
// print(x);
splay(x);
//print(x);
ll cur = (sum[x] + 1) / 2;
while ( x ){
pushdown(x); //在splay上二分查询时一定要pushdown。因为有些标记没有在splay时pushdown
//只要遍历splay就要pushdown,但直接用access和makeroot查值则不需要
if ( ls(x) && sum[ls(x)] >= cur ){ x = ls(x); continue; }
cur -= sum[ls(x)] + w[x];
if ( cur <= 0 ) break;
x = rs(x);
}
return x;
}
node2 query(int x,int y){
if ( dth[x] < dth[y] ) return (node2){0,0};
makeroot(x) , access(y) , splay(x);
return (node2){sum[x],mul[x]};
}
void modify(int x){
makeroot(x) , access(x) , splay(x);
sum[x] = w[x] , mul[x] = d[x] * w[x];
}
}lct;
void clear(){
lct.clear();
for (int i = 1 ; i <= cnt ; i++) e[i].next = e[i].to = 0;
for (int i = 1 ; i <= n ; i++) fa[i] = head[i] = dth[i] = 0,d[i] = 0;
cnt = 0;
}
inline void adde(int x,int y,int w){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].w = w;
head[x] = cnt;
}
void dfs(int x){
for (int i = head[x] ; i ; i = e[i].next){
if ( e[i].to == fa[x] ) continue;
dth[e[i].to] = dth[x] + 1;
d[e[i].to] = d[x] + e[i].w;
jump[0][e[i].to] = fa[e[i].to] = x;
dfs(e[i].to);
}
}
void init(){
for (int i = 1 ; i <= 18 ; i++)
for (int j = 1 ; j <= n ; j++)
jump[i][j] = jump[i - 1][jump[i - 1][j]];
for (int i = 1 ; i <= n ; i++) lct.sum[i] = w[i] , lct.mul[i] = d[i] * w[i];
for (int i = 2 ; i <= n ; i++) lct.link(i,fa[i]);
}
int lca(int x,int y){
if ( dth[x] < dth[y] ) swap(x,y);
for (int i = 18 ; i >= 0 ; i--) if ( dth[jump[i][x]] >= dth[y] ) x = jump[i][x];
if ( x == y ) return x;
for (int i = 18 ; i >= 0 ; i--) if ( jump[i][x] != jump[i][y] ) x = jump[i][x] , y = jump[i][y];
return fa[x];
}
int getson(int x,int y){
for (int i = 18 ; i >= 0 ; i-- ) if ( dth[jump[i][x]] > dth[y] ) x = jump[i][x];
return x;
}
ll getans(int x,int y){
int lca_ = lca(x,y) , mid = lct.query_mid(x,y); ll ans = 0;
if ( lca(x,mid) == mid ){
node2 cur = lct.query(y,lca_);
ans += cur.mul + cur.sum * (d[mid] - 2ll * d[lca_]);
cur = lct.query(fa[mid],getson(x,lca_));
ans += cur.sum * d[mid] - cur.mul;
cur = lct.query(x,mid);
ans += cur.mul - cur.sum * d[mid];
}
else{
node2 cur = lct.query(x,lca_);
ans += cur.mul + cur.sum * (d[mid] - 2 * d[lca_]);
cur = lct.query(fa[mid],getson(y,lca_));
ans += cur.sum * d[mid] - cur.mul;
cur = lct.query(y,mid);
ans += cur.mul - cur.sum * d[mid];
}
return ans;
}
int main(){
freopen("input.txt","r",stdin);
scanf("%d",&T);
while ( T-- ){
clear();
scanf("%d",&n);
for (int i = 1 ; i <= n ; i++) scanf("%d",&w[i]);
for (int i = 1 ; i < n ; i++){
int x,y,w;
scanf("%d %d %d",&x,&y,&w);
adde(x,y,w);
adde(y,x,w);
}
dth[1] = 1 , d[1] = 0, dfs(1) , init();
scanf("%d",&q);
while ( q-- ){
int t,x,y;
scanf("%d %d %d",&t,&x,&y);
if ( t == 1 ) printf("%lld\n",getans(x,y));
else w[x] = y , lct.modify(x);
}
}
return 0;
}
B: lucas定理裸题,也可以打表,拆成二进制找规律
C:贪心,感觉题解有点问题QAQ(没有问题是我自己想错了!)
D:有n次操作(n<=100),每次可将a*=b,或b++,求最后a,b在【L,R】的方案数,L,R<= 1e9
利用性质b <= 100,a只有100以内质数组成,a总共只有2e6个,然后可以dp[i]表示拼出i最少的操作数,统计答案即可
统计次数时从小到大枚举b,用当前b再来更新一次
#include<bits/stdc++.h>
using namespace std;
#define maxn 3000020
#define N 120
#define inf 1e9
typedef long long ll;
int f[maxn],a[maxn],num[maxn],tot;
int prime[N],cnt,tag[N];
int T,L,R,p;
void dfs(int x,int cur,int tag){
if ( cur > 100 && tag ) a[++tot] = cur;// printf("%d %d\n",cur,tot);
if ( x == cnt + 1 ) return;
if ( (ll)cur * prime[x] <= inf ) dfs(x,cur * prime[x],1);
dfs(x + 1,cur,0);
}
void init(){
for (int i = 2 ; i <= 100 ; i++){
if ( !tag[i] ) prime[++cnt] = i;
for (int j = 1 ; j <= cnt && prime[j] * i <= 100 ; j++){
tag[i * prime[j]] = 1;
if ( ( i % prime[j]) == 0 ) break;
}
}
for (int i = 1 ; i <= 100 ; i++) a[++tot] = i;
dfs(1,1,0);
sort(a + 1,a + tot + 1);
memset(f,0x3f,sizeof(f)) , memset(num,0x3f,sizeof(num));
for (int i = 1 ; i <= 100 ; i++) num[i] = i;
num[1] = 0 , f[0] = f[1] = 0;
for (int p = 2 ; p < 100 ; p++){
f[p] = 1; //f数组表示a为i的步数,所以要把b乘给a
for (register int i = 1 , k = 1 ; i <= tot ; i++){
if ( (ll)a[i] * p > inf ) break;
while ( a[k] < (ll)a[i] * p ) k++;
if ( a[k] == (ll)a[i] * p ) f[k] = min(f[k],f[i] + 1);
num[k] = min(num[k],f[k] + p);
}
}
// for (int i = 1 ; i <= 100 ; i++) cout<<num[i]<<endl;
}
int main(){
freopen("input.txt","r",stdin);
freopen("1.out","w",stdout);
scanf("%d",&T);
init();
while ( T-- ){
int ans = 0;
scanf("%d %d %d",&L,&R,&p);
int l = lower_bound(a + 1,a + tot + 1,L) - a , r = upper_bound(a + 1,a + tot + 1,R) - a - 1;
for (int i = l ; i <= r ; i++) if ( num[i] <= p ) ans++;
if ( l == 0 ) ans++;
printf("%d\n",ans);
}
return 0;
}
E:构造图,分类讨论。图相当于拓扑序,即一个点的边集为另一个子集,用斯特林数算组合数,明天写
F:dp计数,好题!有个神奇的优化,分开枚举将n^2转化为n * 2,把位置拆开左右括号分开枚举,但是有括号先枚举防止刚开就闭的无效转移,
2018.9.14理清思路一遍过
#include<bits/stdc++.h>
using namespace std;
#define maxn (1 << 22)
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8
typedef long long ll;
const ll mod = 1e9 + 7;
ll f[220][120][120],fac[220],inv[220];
int n,K,T;
inline ll power(ll x,ll y){
ll res = 1;
while ( y ){
if ( y & 1 ) res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
void init(){
fac[0] = inv[0] = 1;
rep(i,1,100) fac[i] = fac[i - 1] * i % mod , inv[i] = power(fac[i],mod - 2);
}
inline ll C(int n,int m){
if ( n < m ) return 0;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
inline void up(ll &x,ll y){ x = (x + y) % mod; }
int main(){
init();
while ( ~scanf("%d %d %d",&n,&K,&T) ){
memset(f,0,sizeof(f));
f[0][0][0] = 1;
rep(i,0,n * 2 - 1){
rep(k,0,K){
rep(j,0,min(T,(int)k)){
if ( !f[i][j][k] ) continue;
if ( i & 1 ){
rep(l,0,min(T - j,K - k))
up(f[i + 1][j + l][k + l],f[i][j][k] * C(K - k,l));
}
else{
rep(l,0,j)
up(f[i + 1][j - l][k],f[i][j][k] * C(j,l));
}
}
}
}
ll ans = 0;
rep(i,0,T) up(ans,f[n * 2][i][K]);
cout<<ans<<endl;
}
}
G:统计与所有点曼哈顿距离和为C的(u,v)点的个数。固定x,y单调。所以单调扫即可。这么简单的题应该很好想。
所以今天理论上能过4题,实际只过2题。非常不应该。离前5的目标还差很远!!!加油!更加高效的写题!