11.24 game
11.24
又坠机了。
重复 S 组的惨状,t3 假做法调一个半小时,剩半个小时想到正解打完后,\(t2,t4\) 暴力都没来得及打,最后 20 分钟甚至写对拍写挂把电脑整死机了,提前结束比赛。
用心不专,在机房以后非询问不要主动说话了。
比赛应当极大化分数,在努力保证不挂分的前提下应当尽可能先打暴力(暴力和对拍都应该打的快和对),一道题的实现/思考时间超过45分钟应当考虑暂时放弃,把其它分数拿到手后在重新着手。
猜的东西要在纸上明确表明是猜的,免的到后面真认为一定对(像这次 t3 我认为从一个一个删叶子节点有可能正确,实际上是假的)。
结论的正确性取决于是否通过大样例/逻辑上无法证伪。
t1
给定长度为 \(n\) 的字符串 \(S\),字符集大小是 \(26\)。
求有多少种长度为 \(n\) 的回文串 \(T\),其字典序小于等于 \(S\)。
\(1 \leq n \leq 10^7\)。
特判 \(S\) 是否是回文串。
只用考虑 \(T\) 字典序严格小于 \(S\) 的情况。
注意到回文串确定了前半部分,后半部分唯一确定。
枚举 lcp 长度,将 lcp 按是否全在前半部分分类。
全在前半部分,让第一个不同位置 \(T\) 严格小于 \(S\),剩下任取;
与后半部分有交,发现前半部分固定,所以 \(T\) 是唯一的,特判。
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 1e6+10,mo = 998244353;
static char s[N];
static int n;
struct work{
inline bool ok(){
FOR(i,1,n)if(s[i]!=s[n-i+1])return 0;
return 1;
}
char s0[N];
inline bool check(){
FOR(i,(n+1)/2+1,n)if(s0[i]!=s[i])return s0[i]<s[i];
return 0;
}
int pw[N];
inline void init_pw(int n){
pw[0] = 1;
FOR(i,1,n)pw[i] = 1ll*pw[i-1]*26%mo;
}
inline void solve(){
readx(n);
scanf(" %s",s+1);
FOR(i,1,n)s[i]-='A'-1;
int len = (n+1)/2;
//deng
int ans = ok();
//qianbanxiao
FOR(i,1,len)ans = (0ll+ans+1ll*(s[i]-1)*pw[len-i])%mo;
//houbanxiao
FOR(i,1,len)s0[i] = s0[n-i+1] = s[i];
ans = (0ll+ans+check())%mo;
output(ans),putchar(10);
}
void main(){
init_pw(N-1);
int t;for(readx(t);t--;solve());
}
}A;
signed main(){
//file
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
//solve
A.main();
return 0;
}
t2
给定长度为 \(n\) 的序列 \(a\)。
求长度为 \(n\) 的合法序列 \(b\) 的数目,满足 \(\forall i \in [1,n],b_i \in[0,a_i]\),并且 \(\bigoplus_{i=1}^{n}b_i \in [l_i,r_i]\)。
\(1 \leq n \leq 10^5,1 \leq a_i \leq 2^{32}-1\)。
异或和性质+dp好题。
首先答案可以差分。
相当于求 \(\bigoplus_{i=1}^{n}b_i \leq x\) 的方案数。
自然的想法是记 \(f_{i,S}\) 表示从高到低,考虑了第 \(i\) 位,前 \(i\) 位的 \(b_i\) 的异或和等于 \(x\),\(S\) 集合内的数可以没有限制的随便取;转移是枚举 \(n\) 个数怎么取,看该位的异或和是严格小于 \(x\) 还是等于 \(x\) 还是大于 \(x\),但时间复杂度是 \(O(n4^{n}\log V)\) 的(赛时止步于此)。
注意到人为在 \(a\) 序列末尾补充 \(a_{n+1} = x\),则等价于求 \(\bigoplus_{i=1}^{n+1}b_i = 0\) 的方案数。
注意到如果确定了前 \(n\) 个数,那么 \((n+1)\) 个数必然被唯一确定,只需考虑其是否在上限内即可。
假设 \(a_{n+1} = 2^{32}-1\),那么前 \(n\) 个数可以任意取,最后一个数都不会超上限。
记 \(d_i\) 表示没有限制的 \(b_i\) 二进制位上从高到低第一个严格小于 \(a_i\) 的位。
这启发我们找出所有 \(b_i\) 中 \(d_i\) 最大的(多个取 \(i\) 较小的)。对于高于 \(d_i\) 的位,其异或和唯一,可以特判是否为 \(0\);对于 \(d_i-1\) 到 \(0\) 位,\(b_i\) 就能任意取;所以无论其它数 \(d_i-1\) 到 \(0\) 位取什么,\(b_i\) 都能调整至合法;对于第 \(d_i\) 位,可以特殊考虑。
不妨定义 \(f_{k,i,0/1,0/1}\) 表示钦定所有 \(b_i\) 中 \(d_i\) 最大值为 \(d\),考虑了前 \(i\) 个数,第 \(d_i\) 位的异或和为 \(0/1\),是否存在一个 \(d_i=k\) 的方案数。
时间复杂度 \(O((n+q)\log n)\)。
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ll long long
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 1e5+10,M = 60+10,mo = 998244353;
static int n,q;
static ll a[N];
inline void ad(int &x,int y){
if((x+=y)>=mo)x-=mo;
}
struct work1{
int f[M][2][2],g[M][2][2],p[M][2][2];
ll sum;
ll pw[N];
inline void init_pw(int n){
pw[0] = 1;
FOR(i,1,n)pw[i] = pw[i-1]<<1;
}
inline void renew(int f[M][2][2],int g[M][2][2],ll x){
ll t = x;bool flg;
FOR(d,0,61){
flg = t&1;t>>=1;
FOR(i,0,1)FOR(j,0,1)if(g[d][i][j]){
int la = ((x&(pw[d]-1))+1)%mo;
if(flg){
if(j)ad(f[d][i][1],1ll*pw[d]%mo*g[d][i][j]%mo);
else ad(f[d][i][1],g[d][i][j]);
ad(f[d][i^1][j],1ll*la*g[d][i][j]%mo);
}
else ad(f[d][i][j],1ll*la*g[d][i][j]%mo);
}
}
}
inline void get_ans(int f[M][2][2],int &ans,ll now){
ll tmp = pw[62]-1;
FOR(d,0,61){
tmp^=pw[d];
if((now&tmp)==0)ad(ans,f[d][0][1]);
}
if(!now)ad(ans,1);
}
inline int qy(ll x){
FOR(d,0,62)FOR(i,0,1)FOR(j,0,1)g[d][i][j] = p[d][i][j],f[d][i][j] = 0;
renew(f,g,x);
int ans = 0;
get_ans(f,ans,sum^x);
return ans;
}
void main(){
init_pw(62);
FOR(d,0,61)f[d][0][0] = 1;
FOR(tid,1,n){
sum^=a[tid];
std::swap(f,g);
FOR(d,0,61)FOR(i,0,1)FOR(j,0,1)f[d][i][j] = 0;
renew(f,g,a[tid]);
}
FOR(d,0,61)FOR(i,0,1)FOR(j,0,1)p[d][i][j] = f[d][i][j];
for(ll l,r;q--;){
readx(l),readx(r);
ll ans = (0ll+qy(r)-(l?qy(l-1):0))%mo;
output((0ll+ans+mo)%mo),putchar(10);
}
}
}A;
signed main(){
//file
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
//solve
readx(n),readx(q);
FOR(i,1,n)readx(a[i]);
A.main();
return 0;
}
t3
给定 \(n\) 个节点的带边权树,\(m\) 次询问给定 \((a_i,b_i,k_i)\) 三元组,表示将第 \(a_i\) 条边临时修改成 \(b_i\),在树上不重复的选出 \(2k_i\) 对节点 \((x,y)\)(每个节点只能被选择至多 \(1\) 次),然后求这 \(k_i\) 条路径 \((x,y)\) 的交的边权和最大值。
边权非负。
\(1 \leq n,m \leq 5 \times 10^5,1 \leq w_i \leq 10^9\)。
首先注意到路径的交仍然是路径,所以答案必然是一条链。
其次记一条边的权值为两边子树大小的较小值,一条边只有在 \(k_i\) 小于等于其权值时才有可能成为答案中的一条边。
注意到任何时刻合法的边构成了一个连通块,所以答案等价于求连通块的直径。
离线,对 \(k_i\) 从小到大扫描线,用线段树动态维护支持修改边权+删点的直径,时间复杂度 \(O(n \log^2 n)\)。
注意到边权的修改是 \(O(1)\) 的,所以如果将树状数组改成特判,可以做到 \(O(n \log n)\)。
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define GO(x) for(int i = h[x],y = e[i];i;y = e[i=ne[i]])
#define ll long long
#define ve std::vector
#define pb push_back
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 5e5+10,M = 20,nf = 1e9+10;
static int n,q;
static int e[N<<1],ne[N<<1],h[N],tot,w[N<<1],deg[N];
static int now,deta;
inline void add(int x,int y,int ww){
e[++tot] = y,ne[tot] = h[x],h[x] = tot;w[tot] = ww;
++deg[x];
}
struct node1{
int a,b,id;
};
static ve<node1> f[N];
static ve<int> g[N];
static ll ans[N],su[N];
// static int dp[N];
// struct D0{
// ll tr[N];
// void my(int x,int d){
// for(;x<=n;tr[x]+=d,x+=x&-x);
// }
// void my(int l,int r,int d){
// my(l,d),my(r+1,-d);
// }
// ll qy(int x){
// ll ans = 0;
// for(;x;ans+=tr[x],x&=x-1);
// return ans;
// }
// // ll qy(int l,int r){
// // return qy(r)-qy(l-1);
// // }
// }t0;
static int dfn[N],sz[N],bl[N],dep[N],dfsct;
static int st[N][M],pre[N],fa[N];
// static ll su[N];
static int dp[N];
void dfs(int x,int fr){
st[dfn[x]=++dfsct][0] = fr;
dep[x] = dep[fr]+1;
sz[x] = 1;
fa[x] = fr;
pre[dfsct] = x;
GO(x)if(y!=fr){
bl[i/2] = y;su[y] = su[x]+w[i];
dfs(y,x);
sz[x]+=sz[y];
// dp[x] = std::min(dp[x],dp[y]+1);
// t0.my(dfn[y],dfn[y]+sz[y]-1,w[i]);
}
dp[x] = std::min(sz[x],n-sz[x]);
if(sz[x]<n-sz[x])g[dp[x]].pb(x);
else if(fr)g[dp[x]].pb(fr);
}
// void dfs2(int x,int fr){
// GO(x)if(y!=fr)dp[y] = std::min(dp[y],dp[x]+1),dfs2(y,x);
// }
static int pw[N],lg[N];
inline void init_pw(){
lg[0] = -1;FOR(i,1,n)lg[i] = lg[i>>1]+1;
pw[0] = 1;FOR(i,1,lg[n])pw[i] = pw[i-1]<<1;
}
inline int get_min(int x,int y){
return dep[x]<dep[y]?x:y;
}
inline void init_lca(){
FOR(k,1,lg[n])FOR(i,1,n-pw[k]+1)st[i][k] = get_min(st[i][k-1],st[i+pw[k-1]][k-1]);
}
inline int lca(int x,int y){
if(x==y)return x;
if((x=dfn[x])>(y=dfn[y]))std::swap(x,y);
int k = lg[y-x++];
return get_min(st[x][k],st[y-pw[k]+1][k]);
}
inline ll get_su(int x){
return !now||dfn[x]<dfn[now]||dfn[x]>dfn[now]+sz[now]-1?su[x]:su[x]+deta;
}
inline ll dis(int x,int y){
return get_su(x)+get_su(y)-(get_su(lca(x,y))<<1);
}
struct D1{
#define ls (x<<1)
#define rs (x<<1|1)
#define mid ((l+r)>>1)
struct node{
int u,v;ll d;
node(){u = v = d = 0;}
node(int _u,int _v,ll _d){
u = _u,v = _v,d = _d;
}
node(int _u,int _v){
u = _u,v = _v,d = dis(u,v);
}
bool operator < (const node& A)const{
return d<A.d;
}
node operator + (const node& A)const {
if(u==0||v==0)return A;
if(A.u==0||A.v==0)return {u,v,d};
node t = std::max({u,v,d},A);
t = std::max(t,{u,A.u}),t = std::max(t,{u,A.v});
t = std::max(t,{v,A.u}),t = std::max(t,{v,A.v});
return t;
}
}tr[N<<2];
inline void pushup(int x){
tr[x] = tr[ls]+tr[rs];
}
void bd(int x,int l,int r,int *p){
if(l==r)return tr[x]={p[l],p[l],0},void();
bd(ls,l,mid,p),bd(rs,mid+1,r,p);
pushup(x);
}
void my(int x,int l,int r,int pos){
if(r<pos||l>pos)return;
if(l==r)return tr[x]={0,0,0},void();
my(ls,l,mid,pos),my(rs,mid+1,r,pos);
pushup(x);
}
void renew(int x,int l,int r,int pl,int pr){
if(r<pl||l>pr)return;
if(pl<=l&&r<=pr)return;
renew(ls,l,mid,pl,pr),renew(rs,mid+1,r,pl,pr);
pushup(x);
}
ll qy(){
return tr[1].d;
}
#undef ls
#undef rs
#undef mid
}t1;
static bool vis[N];
static int s0[N],tl0,s1[N],tl1,mx;
inline void solve(){
//init
init_pw();
dfs(1,0),init_lca();
t1.bd(1,1,n,pre);
//solve
FOR(i,1,n)if(deg[i]==1)s0[++tl0] = i,vis[i] = 1;
FOR(k,1,mx){
for(auto &t:f[k]){
int a = t.a,b = t.b,id = t.id;
int y = bl[a],prew = w[a*2];
now = y,deta = b-prew;
// t0.my(dfn[y],dfn[y]+sz[y]-1,b-prew);
t1.renew(1,1,n,dfn[y],dfn[y]+sz[y]-1);
ans[id] = t1.qy();
now = 0;
// t0.my(dfn[y],dfn[y]+sz[y]-1,prew-b);
t1.renew(1,1,n,dfn[y],dfn[y]+sz[y]-1);
}
for(auto &x:g[k]){
// printf("x:%d\n",x);
t1.my(1,1,n,dfn[x]);
}
// FOR(tid,1,tl0){
// int x = s0[tid];
// // if(vis[x]||!x)puts("Err"),exit(0);
// // vis[x] = 1;
// t1.my(1,1,n,dfn[x]);
// GO(x)if(!vis[y]&&--deg[y]==1){
// s1[++tl1] = y;vis[y] = 1;
// }
// }
// std::swap(s0,s1),std::swap(tl0,tl1);
// tl1 = 0;
}
FOR(i,1,q){
output(ans[i]),putchar(10);
}
}
signed main(){
//file
freopen("edge.in","r",stdin);
freopen("edge.out","w",stdout);
//rd
tot = 1;
readx(n),readx(q);
int tx,ty,tw;FOR(i,2,n)readx(tx),readx(ty),readx(tw),add(tx,ty,tw),add(ty,tx,tw);
int a,b,k;
FOR(i,1,q){
readx(a),readx(b),readx(k);mx = std::max(mx,k);
f[k].pb((node1){a,b,i});
}
//solve
solve();
return 0;
}
t4
过于困难。

浙公网安备 33010602011771号