给出一个长度为n的01串,你可以在这个01串中添加n - 1个加减号,让整条式子最终的结果绝对值最小
记录当前结果值模拟即可。
给出 n , m , k n,m,k n,m,k,将有 n n n个元素的序列染色,要求对任意连续的长度为 k k k的元素序列,这些元素的颜色都不相同。共有 m m m种颜色,第 i i i颜色需要染 a i a_i ai次,保证 ∑ i = 1 m a i = n \sum_{i = 1}^m a_i = n ∑i=1mai=n。判断是否存在一种染色方案满足上述条件。
将序列分成 n ∣ k n|k n∣k段,除去这 n ∣ k n|k n∣k段元素,还剩下 n n%k n个元素。显然对任意一种颜色,染色次数不能超过 ( n ∣ k + 1 ) (n|k + 1) (n∣k+1)次。染色次数为 ( n ∣ k + 1 ) (n|k + 1) (n∣k+1)次的颜色个数不能超过 n n%k n次。若满足以上条件,则对于每种颜色贪心地不同地序列段里染色,可以保证有解。
#include
using namespace std;
void solve()
{
bool bz = 0;
int n,m,k;
scanf("%d %d %d",&n,&m,&k);
int block = n / k,rem = n % k;
for(int i = 1;i <= m;i ++)
{
int x;
scanf("%d",&x);
if(x <= block)
continue;
else
{
if(x == block + 1 && rem)
rem --;
else
bz = 1;
}
}
if(bz)
{
printf("NO\n");
return;
}
printf("YES\n");
}
int main()
{
int T;
scanf("%d",&T);
while(T --)
solve();
}
n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1\leq n \leq2\cdot10^5) n(1≤n≤2⋅105)个人玩游戏,第 i i i个人的价值为 i i i。规定游戏规则用 0 , 1 0,1 0,1表示。若游戏规则为 0 0 0则价值低的人获胜,若游戏规则为 1 1 1则价值高的人获胜。每一轮选择仍然存活的任意两个人进行比赛,输的人死亡,赢的人存活。游戏一共会进行 n − 1 n - 1 n−1轮,最后存活的人为胜利者。
给出一个长度为 n − 1 n - 1 n−1的 01 01 01串 s s s,若有 x x x个人参加游戏,则游戏会进行 x − 1 x - 1 x−1 轮,每轮游戏规则为 01 01 01串的前 x − 1 x - 1 x−1个字符。
对于所有 x ∈ [ 2 , n ] x\in[2,n] x∈[2,n],求当有 x x x个人参加游戏时,有多少人可能获得胜利。
考虑当有 x x x个人参加游戏时,序号为 i i i即价值为 i i i的人怎样才能存活。整个比赛过程可以看做一个长为 x x x的序列,然后不断去除元素。考虑最理想的情况,两种规则可以看做对序列的以下两种操作。
- 对于规则0,去除一个元素,这个元素至少存在一个比他价值低的元素
- 对于规则1,去除一个元素,这个元素至少存在一个比他价值高的元素。
我们要判断是否存在一种操作序列使得最后价值为 i i i的元素保留。
发现去除元素这个操作十分麻烦且没有规律,考虑逆向思维,将删除元素的操作转化成增加元素的操作。既然我们要保证最后元素 i i i存活,不妨将问题传化成:一开始有一个元素 i i i,你可以进行以下两种操作
- 对于规则0,你可以任意添加一个未出现过、当前已存在比它价值小的元素,且小于等于 x x x的元素,使其加入序列
- 对于规则1,你可以选择任意一个未出现过、当前已存在比它价值大的元素,且大于等于 1 1 1的元素,使其加入序列。
判断是否存在一种操作序列使得可以这个一开始只有元素 i i i的序列还原成包含 1 1 1至 x x x的序列。
贪心来想,对于第一次规则为 0 0 0的比赛,我一定会选择让 x x x加入序列(如果可以加入的话),这样可以保证之后规则为 1 1 1的比赛都是合法的(因为 x x x是最大的,所以你可以选择任何元素添加),反之亦然。也就是说,只要我成功使得序列里存在 1 1 1和存在 x x x,那么剩下的操作(无论是 0 0 0还是 1 1 1)都是可以合法的。
因此,只需要记录 01 01 01串 s s s的后缀 0 0 0和后缀 1 1 1的个数,对于一个可能胜利的元素, 1 1 1至 x x x中比它大的元素个数不能超过后缀 0 0 0的个数,比它小的元素个数不能超过后缀 1 1 1的个数。
#include
using namespace std;
void solve()
{
int n;
scanf("%d",&n);
string s;
cin>>s;
int cnt0 = 0,cnt1 = 0;
for(int i = 0;i < n - 1;i ++)
{
if(s[i] == '0')
cnt0 ++,cnt1 = 0;
else
cnt0 = 0,cnt1 ++;
int mx = i + 2;
printf("%d ",mx - max(cnt0,cnt1));
}
printf("\n");
}
int main()
{
int T;
scanf("%d",&T);
while(T --)
solve();
}
给出一个 n n n行 m m m列 ( n ⋅ m ≤ 1 ⋅ 1 0 6 ) (n\cdot m \leq 1\cdot10^6) (n⋅m≤1⋅106)的 01 01 01矩阵 a a a,你可以进行如下操作:
选择第 i i i行和第 j j j行,以及 p o s pos pos,交换 a i , p o s 和 a j , p o s a_{i,pos}和a_{j,pos} ai,pos和aj,pos。
若干次操作后我们希望这个01矩阵每一行中,值为 1 1 1的列数个数相同。
求最少的操作次数,并输出操作方案。若不存在任何一种操作使得条件满足,则输出 − 1 -1 −1
交换并不会改变 1 1 1的个数,令 1 1 1的个数为 t o t tot tot,若 t o t % n ! = 0 tot \% n != 0 tot%n!=0,则无解。
显然,每一行 1 1 1的个数为 t o t / n tot / n tot/n,若当前矩阵不满足情况,必然存在至少一行 1 1 1的个数大于 t o t / n tot/n tot/n,以及至少一行 1 1 1的个数小于 t o t / n tot/n tot/n。贪心一下,将含有 1 1 1的个数较多的行中价值为 1 1 1的列与含有 1 1 1的个数较少的行中价值为 0 0 0的列交换总是最优的。由鸽巢原理,总会存在这样的位置。模拟即可。
#include
using namespace std;
#define pii pair<int,int>
#define fi first
#define se second
void solve()
{
int n,m,cnt = 0;
scanf("%d %d",&n,&m);
vector<vector<int>> ar(n,vector<int>(m));
vector<int> p(n,0);
for(int i = 0;i < n;i ++)
for(int j = 0;j < m;j ++)
{
scanf("%d",&ar[i][j]),p[i] += ar[i][j],cnt += ar[i][j];
}
if(cnt % n != 0)
{
printf("-1\n");
return;
}
int stp = 0,exp = cnt / n;
queue<int> less,more;
for(int i = 0;i < n;i ++)
{
if(p[i] > exp)
stp += p[i] - exp,more.push(i);
if(p[i] < exp)
stp += exp - p[i],less.push(i);
}
stp /= 2;
printf("%d\n",stp);
while(less.size() || more.size())
{
int ls = less.front(),mr = more.front();
less.pop(),more.pop();
for(int j = 0;j < m;j ++)
{
if(p[ls] == exp || p[mr] == exp)
break;
if(ar[ls][j] == 0 && ar[mr][j] == 1)
{
ar[ls][j] = 1;
ar[mr][j] = 0;
p[ls] ++;
p[mr] --;
printf("%d %d %d\n",ls + 1,mr + 1,j + 1);
}
}
if(p[ls] != exp)
less.push(ls);
if(p[mr] != exp)
more.push(mr);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T --)
solve();
}
给你一棵有 n ( 2 ≤ n ≤ 2 ⋅ 1 0 5 ) n(2\leq n \leq 2\cdot 10^5) n(2≤n≤2⋅105)个结点的树,树上有两颗棋子。开始时两颗棋子A、B都在结点 1 1 1。你可以进行如下操作:每次操作,你可以移动其中一棵棋子,使其走到相邻的一个结点。在移动过程中,两颗棋子的距离任何时候都不能超过 d ( 2 ≤ d ≤ 2 ⋅ 1 0 5 ) d(2\leq d \leq 2\cdot 10^5) d(2≤d≤2⋅105)。
对棋子A,给出有 m 1 ( 1 ≤ m 1 ≤ n ) m_1(1\leq m_1 \leq n) m1(1≤m1≤n)个目标结点的数组 a a a,你需要以任意顺序使棋子A经过所有目标结点。
对棋子B,给出有 m 2 ( 1 ≤ m 2 ≤ n ) m_2(1\leq m_2 \leq n) m2(1≤m2≤n)个目标结点的数组 b b b,你需要以任意顺序使棋子B经过所有目标结点。
最终棋子还必须回到结点 1 1 1,询问最少的操作次数。
对于A棋子的某个目标结点 x x x,B必须经过 x x x的往上跳 d d d层的祖先,否则就会超出距离。对棋子B同理。
将这些A、B必须经过的附加点加入各自的目标结点序列,然后将两个目标结点序列合并,并按照 d f n dfn dfn序大小排序。可以发现,按照排序后目标结点序列的顺序进行操作是最优的,且总是存在一种操作方案可以满足这个操作序列。之后就可以模拟了。
可能存在某种特殊情况,比如说某个目标结点往上跳 k k k次的祖先不存在。但是由于最终两颗棋子都会回到根节点,所以这种情况并不会影响计数。
#include
using namespace std;
#define N 1000000 + 200
#define pii pair<int,int>
#define fi first
#define se second
int f[N][21];
struct Tree
{
std::vector<int> sz, top, dep, fa, dfn,tim,in,out;
int cur,curr;
std::vector<std::vector<int>> r;
Tree(int n) : sz(n + 1), top(n + 1), dep(n + 1), fa(n + 1,0), r(n + 1), dfn(n + 1),cur(0),in(n + 1),out(n + 1) ,curr(0){}
void addEdge(int u, int v)
{
r[u].push_back(v);
r[v].push_back(u);
}
void init() {
dfsSz(1);
top[1] = 1;
dfsHLD(1);
}
void dfsSz(int u)
{
if (fa[u] != 0)//删掉连接父亲的边
r[u].erase(std::find(r[u].begin(), r[u].end(), fa[u]));
sz[u] = 1;
for (int &v : r[u])
{
fa[v] = u;
dep[v] = dep[u] + 1;
dfsSz(v);
sz[u] += sz[v];
if (sz[v] > sz[r[u][0]])
std::swap(v, r[u][0]);
}
}
void dfsHLD(int u)
{
f[u][0] = fa[u];
for(int i = 1;i <= 20;i ++)
f[u][i] = f[f[u][i - 1]][i - 1];
dfn[u] = ++cur;
in[u] = ++ curr;//欧拉序
for (int v : r[u])
{
if (v == r[u][0])
top[v] = top[u];
else
top[v] = v;
dfsHLD(v);
}
out[u] = ++curr;//欧拉序
}
int getlca(int u, int v) //多个点lca为dfn最大和最小的两个点的lca
{
//如果u是v的祖先或v是u的祖先,那么直接返回祖先,可使时间复杂度降低很多
if(in[u] <= in[v] && out[u] >= out[v])
return u;
if(in[v] <= in[u] && out[v] >= out[u])
return v;
//正常重链剖分找lca
while (top[u] != top[v])
{
if (dep[top[u]] > dep[top[v]])
u = fa[top[u]];
else
v = fa[top[v]];
}
if (dep[u] < dep[v])
return u;
else
return v;
}
int dis(int u,int v)
{
int lca = getlca(u,v);
return dep[u] + dep[v] - 2 * dep[lca];
}
int d_fa(int u,int d)
{
int rem = d,nw = u;
for(int i = 20;i >= 0;i --)
if(rem & (1<<i))
{
rem -= (1<<i);
if(f[nw][i] == 0)
{
return 1;
}
nw = f[nw][i];
}
return nw;
}
};
int main()
{
int n,m,d;
vector<int> a;
vector<int> b;
scanf("%d %d",&n,&d);
Tree tr(n);
for(int i = 1;i <= n - 1;i ++)
{
int u,v;
scanf("%d %d",&u,&v);
tr.addEdge(u,v);
}
tr.init();
scanf("%d",&m);
for(int i = 1;i <= m;i ++)
{
int x;
scanf("%d",&x);
a.push_back(x);
b.push_back(tr.d_fa(x,d));
}
scanf("%d",&m);
for(int i = 1;i <= m;i ++)
{
int x;
scanf("%d",&x);
b.push_back(x);
a.push_back(tr.d_fa(x,d));
}
vector<pii> node;
for(auto v:a)
{
node.push_back({v,0});
}
for(auto v:b)
{
node.push_back({v,1});
}
sort(node.begin(),node.end(),[&](pii x,pii y){
return tr.dfn[x.fi] < tr.dfn[y.fi];
});
int resa = 1,resb = 1,stp = 0;
for(auto v:node)
{
if(v.se == 0)
{
stp += tr.dis(resa,v.fi);
resa = v.fi;
}
else
{
stp += tr.dis(resb,v.fi);
resb = v.fi;
}
}
stp += tr.dis(resa,1) + tr.dis(resb,1);
printf("%d\n",stp);
}
有一个魔术师,他会进行 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1\leq n\leq2\cdot 10^5) n(1≤n≤2⋅105)次操作。操作有 3 3 3种:
- 1 x 1\ x 1 x:创造一个生命值为 x ( 1 ≤ x ≤ 2 ∗ 1 0 5 ) x(1\leq x \leq 2*10^5) x(1≤x≤2∗105)的小猪
- 2 x 2\ x 2 x:将所有小猪的生命值减去 x ( 1 ≤ x ≤ 2 ∗ 1 0 5 ) x(1\leq x \leq 2*10^5) x(1≤x≤2∗105)
- 3 3 3:按顺序重复之前做过的所有操作(包括之前做过的操作3)
如果一只小猪的生命值等于或低于 0 0 0,那么它就会死掉。
给出 n n n次操作,问 n n n次操作时候,还有多少小猪活着。
模拟可以得到每次操作过后,各种生命值的小猪还剩下多少只,但这样显然会超时。
考虑一次操作 3 3 3会造成什么影响。倘若在进行某次操作 3 3 3之前,我们已知前面所有操作做完之后还剩下多少小猪,以及他们的生命值。假设对于生命值为 h i h_i hi的小猪有 n u m i num_i numi只,此次操作 3 3 3之前的操作会对猪猪造成总共 t o t tot tot的伤害,那么此次操作 3 3 3可以看作将这 n u m i num_i numi只生命值为 h i h_i hi的小猪克隆成 n u m i num_i numi只生命值为 h i − t o t h_i - tot hi−tot的小猪(原先的小猪)以及 n u m i num_i numi只生命值为 h i h_i hi的小猪(新的小猪)。对于 t o t tot tot值的计算,可以在每次操作 2 2 2时将 x x x值累加,每次操作 3 3 3时将 t o t ∗ 2 tot * 2 tot∗2。由于 h i h_i hi比较小,至多经过 l o g log log次操作 3 3 3, t o t tot tot就会超过小猪的最大生命值,因此暴力模拟即可。
#include
using namespace std;
map<long long,long long> M;
const long long mod = 998244353;
int n;
long long mul = 1,inv,ct = 1,tot,dam;
bool bz;
long long qpow(long long x,long long t)
{
if(t == 0)
return 1ll;
else
{
if(t % 2)
return x * qpow(x,t - 1ll) % mod;
else
{
long long tmp = qpow(x,t / 2);
return tmp * tmp % mod;
}
}
}
void add(long long &x,long long y)
{
x = (x + y) % mod;
}
int main()
{
scanf("%d",&n);
inv = qpow(2,mod - 2);
for(int i = 1;i <= n;i ++)
{
int op;
scanf("%d",&op);
if(op == 1)
{
long long x;
scanf("%lld",&x);
//printf("%lld %lld\n",tot + x,tot);
add(M[tot + x],ct);
}
if(op == 2)
{
long long x;
scanf("%lld",&x);
tot += x;
}
if(op == 3)
{
if(tot > 200000)
continue;
if(tot == 0)
mul = mul * 2ll % mod,ct = ct * inv % mod;
else
{
for(int i = 200000 + tot;i > tot;i --)
{
if(M.count((long long)i))
add(M[i + tot],M[i]);
}
tot *= 2;
}
}
}
long long cnt = 0;
for(auto v:M)
{
if(v.first > tot)
cnt = (v.second + cnt) % mod;
}
cnt = (cnt * mul) % mod;
printf("%lld\n",cnt);
}