E. Black and White Tree 未补
E. Purple Crayon 未补
E1. Cats on the Upgrade (easy version) 未补
整体二分 未补
D2. Game on Sum 未补
D. Flipping Range 未补
牛客练习赛95 gcd 代码
牛客练习赛95 Painting Fences 代码
E. New School 代码
E. Middle Duplication 代码
就枚举r从0-k,然后算出那个式子的最小值的最大值,最小值是看b的最大值,因为b如果可以染x个节点,那么可以把一个树砍掉根节点也是可以的,那就可以染x-1个节点,所以要看b可以染的最大值最大值采取贪心,根据每个节点的贡献来贪。
假设一棵树的最大深度子节点是x那么这棵树的其它节点被选之前一定要选x,所以其他节点的贡献就从新开始算,这样算这真好,原本还想线段树+dfs序查询区间最小值和标记区间减1,但是看其他人代码也不像,所以想到这玩意,不过好像也可以另一种贪心也是对的,不知道咋证明,看代码吧。。。
int siz[N], deep[N], son[N];
vector<int>G[N], ans;
int dfs(int u, int fa, int d) // siz是子树的最大深度,son 是子树中有最大深度的那颗子树;
{
if(G[u].size() == 1 && G[u][0] == fa) return siz[u] = d;
for(auto x : G[u])
{
if(x == fa) continue;
siz[u] = max(siz[x], dfs(x, u, d+1));
if(siz[son[u]] < siz[x]) son[u] = x;
}
return siz[u];
}
void dfs1(int u, int fa)
{
deep[son[u]] = deep[u] + 1;
son[u] ? dfs1(son[u], u) : ans.push_back(deep[u]);
for(auto x : G[u])
{
if(x == fa||x == son[u]) continue;
deep[x] = 1;
dfs1(x, u);
}
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for(int i = 2;i <= n;i ++)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v); G[v].push_back(u);
}
deep[1] = 1; dfs(1, 0, 0); dfs1(1, 0);
sort(ans.begin(), ans.end(), greater<int>());
int b = n;
LL x = -0x3f3f3f3f3f3f3f3f;
for(int i = 0;i < k;i ++)
{
if(i < ans.size()) b -= ans[i];
int temp = min(b, n>>1);
x = max(x, (LL)(temp+i+1-n)*(temp-i-1));
}
cout<<x<<endl;
return 0;
}
// 另一种贪心。
// 按照字数大小贪心。
void dfs(int u, int fa)
{
siz[u] = 1;
for(auto x : G[u])
{
if(x == fa) continue;
dfs(x, u);
siz[u] += siz[x];
if(siz[son[u]] < siz[x]) son[u] = x;
}
}
…期望线性,E(ans) =
∑
i
=
2
n
\sum_{i=2}^{n}
∑i=2n E(a[i] ≠ a[i-1]) =
∑
i
=
2
n
\sum_{i=2}^{n}
∑i=2n 1 - E(a[i] = a[i-1]);
第一个等号就没想到
void add(int &u, LL v){u = (u+v)%mod;return ;}
void mull(int &u, LL v){u = u*v%mod;return ;}
int qsm(LL u, int v){LL ans = 1;for(;v;v>>=1,u=u*u%mod)if(v&1)ans=ans*u%mod;return ans;}
int inv(int u){return qsm(u, mod-2);}
map<pair<int,int>, bool>ma;
int c[N], x[N], y[N], num[N]; // c[i] i和i-1相同的情况,num[i] i可选的数量;
int v(int i){return mod-(LL)c[i]*inv((LL)num[i-1]*num[i]%mod)%mod;} // c/num/num;
int main()
{
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
int ans = n;
for(int i = 1;i <= n;i ++) num[i] = m;
for(int i = 2;i <= n;i ++) c[i] = m, add(ans, v(i));
cout<<ans<<endl;
for(int i = 1;i <= q;i ++)
{
int f, id;
scanf("%d", &f);
if(f&1){
scanf("%d%d", x+i, y+i);
bool a = ma[{x[i]-1, y[i]}], b = ma[{x[i], y[i]}], d = ma[{x[i]+1, y[i]}];
add(ans, -v(x[i])), add(ans, -v(x[i]+1)), num[x[i]] --;
if(x[i] > 1) c[x[i]] -= (!a&&!b), add(ans, v(x[i]));
if(x[i] < n) c[x[i]+1] -= (!b&&!d), add(ans, v(x[i]+1));
}
else{
scanf("%d", &id), x[i] = x[id], y[i] = y[id];
bool a = ma[{x[i]-1, y[i]}], b = ma[{x[i], y[i]}], d = ma[{x[i]+1, y[i]}];
add(ans, -v(x[i])), add(ans, -v(x[i]+1)), num[x[i]] ++;
if(x[i] > 1) c[x[i]] += (a^b), add(ans, v(x[i]));
if(x[i] < n) c[x[i]+1] += (b^d), add(ans, v(x[i]+1));
}
ma[{x[i], y[i]}] = 1 - ma[{x[i], y[i]}];
add(ans, mod);
printf("%d\n", ans);
}
return 0;
}
∑ i = 1 n ∏ j = 1 n R [ i ] i − L [ i ] − 1 i \sum_{i=1}^n \prod_{j=1}^n{\frac{R[i]}{i}-\frac{L[i]-1}{i}} ∑i=1n∏j=1niR[i]−iL[i]−1 交换顺序,再分块再前缀乘
const int N = 3e5+10, mod = 998244353; // 1e9+7 998244353
void add(int &u, LL v){u = (u+v)%mod;return ;}
void mull(int &u, LL v){u = u*v%mod;return ;}
int qsm(LL u, int v){LL ans = 1;for(;v;v>>=1,u=u*u%mod)if(v&1)ans=ans*u%mod;return ans;}
int inv(int u){return qsm(u, mod-2);}
int st[N], in[N], ze[N];
int main()
{
int n;
scanf("%d", &n);
for(int i = 0;i <= 3e5;i ++) st[i] = 1, in[i] = inv(i);
for(int i = 1;i <= n;i ++)
{
int x, y;
scanf("%d%d", &x, &y);
x --;
st[y+1] = 0;
for(int l = 1, r;l <= y;l = r + 1)
{
r = min(y/(y/l) , l<=x?x/(x/l) : N);
int t = y/l-x/l;
if(t) mull(st[l], t), mull(st[r+1], in[t]);
else ze[l] ++, ze[r+1] --; // 可能某一段是0但是后面不是0
}
}
int ans = 0;
for(int i = 1;i <= 3e5;i ++)
mull(st[i], st[i-1]), add(ze[i], ze[i-1]), add(ans, ze[i] ? 0 : st[i]);
cout<<ans<<endl;
return 0;
}
先贪心,每组一个老师,然后有两种思路
一种是分开来看,那就是第一大的老师指导第一大的同学,第二对第二…, 看一组去掉一个数后应该在哪里,这时候就要预处理一段区间统一往右或往左是否可行,代码如下。
第二种是放到一起,m和m个PLI (pair<LL,int>)都放到一个数组里面然后排序按照平均值,老师平均值就是它本身,平均值相同老师较大,然后从后往前遍历,是学生那样-1,是老师那样+1,这样对不考虑变化的情况,是否可行就是m个组都大于等于0,然后改变的话就是区间最小值是否大于一个数(0/1),大概这种方法是这种思路,这种好像只用个st表,没写.
bool L[N], R[N]; // L,R 是不移动的情况下 1-i对1-i是否可行 和 i-m对i-m是否可行。
int a[N], l[N], r[N]; // l,r 是左移动或右移动的前缀和;
vector<int>v[N];
struct point{
LL x, y;
friend bool operator < (const point a, const point c){return a.x*c.y < a.y*c.x;}
}c[N];
int main()
{
int t;
scanf("%d", &t);
for(int _ = 1;_ <= t; _ ++)
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1;i <= n;i ++) scanf("%d", a+i);
sort(a+1, a+n+1);
for(int i = 1;i <= m;i ++) a[i] = a[i+n-m];
for(int i = 1;i <= m;i ++)
{
int len, t;
scanf("%d", &len);
c[i].y = len; c[i].x = 0; v[i].clear();
for(int j = 1;j <= len;j ++)
{
scanf("%d", &t);
v[i].push_back(t); c[i].x += t;
}
}
sort(c+1, c+m+1);
L[0] = R[m+1] = 1; // 预处理
for(int i = 1;i <= m;i ++)
(l[i] = 1LL*a[i-1]*c[i].y >= c[i].x),
(r[i] = 1LL*a[i+1]*c[i].y >= c[i].x),
L[i] = R[i] = 1LL*a[i]*c[i].y >= c[i].x;
for(int i = 1;i <= m;i ++) L[i] &= L[i-1], l[i] += l[i-1], r[i] += r[i-1];
for(int i = m;i >= 1;i --) R[i] &= R[i+1];
for(int i = 1;i <= m;i ++)
{
LL sum = 0;
for(auto x : v[i]) sum += x;
int siz = v[i].size(), pos = lower_bound(c+1, c+m+1, point{sum, siz}) - c;
for(auto x : v[i])
{
int k = upper_bound(c+1, c+m+1, point{sum-x, siz-1}) - c - 1; // 这里面有点玄学。。。。
bool f = 1;
if(k < pos){
k ++;
f = (r[pos-1]-r[k-1] == pos-1-k+1)&L[k-1]&R[pos+1];
}
else if(k >= pos) f = (l[k]-l[pos] == k-pos)&L[pos-1]&R[k+1];
printf("%d", f&(1LL*a[k]*(siz-1) >= sum-x));
}
}
puts("");
}
return 0;
}