hdu7138-7149
利用exkmp把s的每个后缀与s的lcp求出来
设后缀 [ i , n ] [i,n] [i,n]和 s s s的 l c p lcp lcp长度为 x x x
那么只有当 i ≤ x i \le x i≤x 时,会产生贡献,中间重合部分为 k k k的倍数的时候有贡献
找到第一个和最后一个 k k k的倍数的位置,把贡献差分一下即可
#include
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e6+5;
char s[maxn];
int n,z[maxn],k;
ll tag[maxn];
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++) z[i]=tag[i]=0;
z[1]=n;
for(int i=2,l=0,r=0;i<=n;i++)
{
if(i<=r) z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n && s[i+z[i]]==s[z[i]+1]) z[i]++;
if(i+z[i]-1>r) l=i,r=i+z[i]-1;
}
scanf("%d",&k);
for(int i=0;i=i+k)
{
com=((com-i)/k)*k;
tag[2*i+k]++;
if(2*i+k+com<=n) tag[2*i+k+com]--;
}
}
ll ans=1;
for(int i=1;i<=n;i++)
{
if(i+k<=n) tag[i+k]+=tag[i];
ans=ans*(tag[i]+1)%mod;
}
printf("%lld\n",ans);
}
return 0;
}
直接暴力枚举边集的选择情况,然后dfs判断是否能到达即可
时间复杂度 O ( 2 n n 2 ) O(2^nn^2) O(2nn2)
#include
using namespace std;
typedef long long ll;
int n,m,k;
int sx,sy,ex,ey;
int ans;
int p[18];
int ax[18],ay[18],bx[18],by[18];
int ban[257][257],id[16][16],cnt;
int vis[18][18];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
void dfs(int x,int y)
{
vis[x][y]=1;
// printf("%d\n",n);
for(int d=0;d<4;d++)
{
int tox=x+dx[d];
int toy=y+dy[d];
if(tox<0 || toy<0 || tox>=n || toy>=m || ban[id[x][y]][id[tox][toy]]) continue;
if(vis[tox][toy]) continue;
dfs(tox,toy);
}
}
bool check()
{
// printf("%d\n",n);
for(int i=1;i<=k;i++)
{
if(p[i]) continue;
if(ax[i]==bx[i])
{
int ymin=min(ay[i],by[i]);
int ymax=max(ay[i],by[i]);
for(int j=ymin;jans) return;
if(check()) ans=gs;
// printf("[%d :%d]\n",gs,vis[ex][ey]);
return;
}
p[step+1]=0; dfs(step+1);
p[step+1]=1; dfs(step+1);
}
int main()
{
// freopen("a.in","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
cnt=0;
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
id[i][j]=++cnt;
scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
for(int i=1;i<=k;i++)
scanf("%d%d%d%d",&ax[i],&ay[i],&bx[i],&by[i]);
ans=k;
dfs(0);
// printf("[%d]\n",n);
printf("%d\n",ans);
// return 0;
}
return 0;
}
n个物品,有体积和价值,给定容量为m的包,询问把包装满的最大xor价值
设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示前i个物品,异或和为j,体积为k的是否可行
那么对于一个物品 ( v , w ) (v,w) (v,w),转移为 f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j ] [ k ] ∣ f [ i − 1 ] [ j x o r v ] [ k − v ] f[i][j][k]=f[i-1][j][k]|f[i-1][j\ xor\ v][k-v] f[i][j][k]=f[i−1][j][k]∣f[i−1][j xor v][k−v]
可以用bitset优化
#include
using namespace std;
int n,m;
bitset <1025> f[2][1025],emp;
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
int x,y;
for(int i=0;i<1024;i++)
f[0][i]=f[1][i]=emp;
int p=1;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
for(int j=0;j<1024;j++)
f[p][j]=f[p^1][j]|(f[p^1][j^y]<
给定坐标范围为 ( 1 , 1 ) − ( m , m ) (1,1)-(m,m) (1,1)−(m,m)的n个点,求有多少三个无序点对满足曼哈顿距离的中位数是质数
我们把 n 2 n^2 n2条边拿出来排序,把遍历过的边设成黑色,未经历过的设成白色,那么我们要求的就是一个点,到当前边的两个点 ( u , v ) (u,v) (u,v),一条黑一条白的,每个点维护2个bitset表示连接点是黑/白的点集
时间复杂度 O ( n 3 w ) O(\frac{n^3}{w}) O(wn3)
#pragma GCC optimize(3)
#include
using namespace std;
const int maxn=2e3+5;
int n,m;
bool vis[200005];
int pr[200005],cnt,tot;
inline int read()
{
int x=0,f=1;
char cc=getchar();
while(cc!='-' && (cc<'0' || cc>'9')) cc=getchar();
if(cc=='-') f=-1,cc=getchar();
while(cc>='0' && cc<='9') x=x*10+cc-'0',cc=getchar();
return x*f;
}
void init()
{
vis[1]=1;
for(int i=2;i<2e5+5;i++)
{
if(!vis[i])
pr[++tot]=i;
for(int j=1;j<=tot && 1ll*pr[j]*i<2e5+5;j++)
{
vis[i*pr[j]]=1;
if(i%pr[j]==0) break;
}
}
}
struct point
{
int x,y;
}p[maxn];
struct edge
{
int x,y;
int len;
}e[maxn*maxn];
bool cmp(edge x,edge y)
{
return x.len W[maxn],B[maxn];
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int T; scanf("%d",&T);
init();
while(T--)
{
scanf("%d%d",&n,&m); cnt=0;
// for(int i=1;i<=n;i++) p[i].x=read(),p[i].y=read();
for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y);
for(int i=1;i<=n;i++) B[i].reset(),W[i].reset();
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
e[++cnt].x=i,e[cnt].y=j,e[cnt].len=dist(p[i],p[j]);
W[i].set(j); W[j].set(i);
}
// printf("[%d]\n",cnt);
sort(e+1,e+cnt+1,cmp);
long long ans=0;
for(int i=1;i<=cnt;i++)
{
// printf("%d\n",e[i].len);
if(!vis[e[i].len]) ans+=(W[e[i].x]&B[e[i].y]).count()+(W[e[i].y]&B[e[i].x]).count();
W[e[i].x].set(e[i].y,0); W[e[i].y].set(e[i].x,0);
B[e[i].x].set(e[i].y,1); B[e[i].y].set(e[i].x,1);
}
printf("%lld\n",ans);
}
return 0;
}
题目规定了所有简单环都是奇环,所以不存在有环贴环(任意两个环没有公共边)
否则,若有公共边,两个环剩下的部分奇偶性相同,会有成一个新的偶环,不合题意
那么我们的图就是一个仙人掌
利用莫反可以把 gcd = k \gcd =k gcd=k的问题转换为求 k ∣ gcd k\ | \gcd k ∣gcd的问题
我们把所有 i i i的倍数边拿出来,缩边双后,在DAG上dp计算路径数量
#include
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=2e5+5;
const int maxv=1e5+5;
int n,m,L;
struct edge
{
int u,v,w;
}e[maxn];
int pr[maxv],cnt,mu[maxn];
bool vis[maxv];
vector fac[maxv],q[maxn];
void init()
{
mu[1]=1;
for(int i=2;i G[maxn],GG[maxn<<1];
int dfn[maxn],low[maxn],dfstime;
int tot,st[maxn],top;
ll d[maxn];
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++dfstime;
d[u]=0; st[++top]=u;
for(int to:G[u])
{
if(!dfn[to])
{
tarjan(to,u);
low[u]=min(low[u],low[to]);
d[u]+=d[to];
if(low[to]==dfn[u]) // bridge edge
{
++tot;
GG[tot].clear();
int v=st[top];
while(v!=to)
{
GG[tot].push_back(v);
GG[v].push_back(tot);
top--; v=st[top];
}
top--; GG[tot].push_back(to);
GG[to].push_back(tot);
GG[tot].push_back(u);
GG[u].push_back(tot);
}
}
else
{
low[u]=min(low[u],dfn[to]);
if(to!=fa) d[u]++,d[to]--;
}
}
}
ll f[maxn<<1];
void add(ll &x,ll y)
{
x=(x+y)%mod;
}
void calc(int u,int fa,int id)
{
for(int to:GG[u])
{
if(to==fa) continue;
calc(to,u,id);
}
if(u<=n)
{
f[u]=1;
for(int to:GG[u])
{
if(to==fa) continue;
add(ans[id],f[u]*f[to]%mod);
add(f[u],f[to]);
}
}
else if(GG[u].size()==2)
{
f[u]=0;
for(int to:GG[u])
{
if(to==fa) continue;
add(f[u],f[to]);
}
}
else
{
f[u]=0;
for(int to:GG[u])
{
if(to==fa) continue;
add(ans[id],f[u]*f[to]%mod);
add(f[u],2ll*f[to]%mod);
}
}
}
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int T; scanf("%d",&T);
init();
while(T--)
{
scanf("%d%d%d",&n,&m,&L);
int x,y,z;
for(int i=1;i<=L;i++) ans[i]=0,q[i].clear();
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
for(int j:fac[e[i].w]) q[j].push_back(i);
}
vector p;
for(int i=1;i<=L;i++)
{
p.clear();
for(int id:q[i])
{
int u=e[id].u,v=e[id].v;
G[u].clear(); G[v].clear();
GG[u].clear(); GG[v].clear();
dfn[u]=dfn[v]=0; p.push_back(u); p.push_back(v);
}
for(int id:q[i])
{
int u=e[id].u,v=e[id].v;
G[u].push_back(v); G[v].push_back(u);
}
tot=n; dfstime=top=0;
for(int u:p)
if(!dfn[u]) tarjan(u,0),calc(u,0,i);
// printf("[%d]:%d %lld\n",i,tot,ans[i]);
// for(int j=1;j<=tot;j++)
// {
// printf("%d: ",j);
// for(int k:GG[j]) printf("%d ",k);
// printf("\n");
// }
}
for(int i=1;i<=L;i++)
{
for(int j=2;i*j<=L;j++)
ans[i]+=1ll*mu[j]*ans[i*j];
ans[i]=(ans[i]%mod+mod)%mod;
}
ll res=0;
for(int i=1;i<=L;i++) res^=ans[i];
printf("%lld\n",res);
}
return 0;
}
我们先建立kruskal重构树
那么一次询问就相当于查询子树内所有类型的物品最大价值的和
注意到每种物品最多出现10次,我们对一个种类的物品建立虚树,维护每个点的上一个同种祖先的位置,给链做加法统计贡献,利用树状数组维护单点修改,子树求和即可
修改操作同理
时间复杂度 O ( 10 n log n ) O(10n\log n) O(10nlogn)
#include
using namespace std;
typedef long long ll;
const int maxn=400005;
struct edge
{
int u,v,w;
}e[maxn];
int n,m,fa[21][maxn],w[21][maxn],q;
int f[maxn],c[maxn];
int dfn[maxn],mx[maxn],dep[maxn];
int sta[maxn],top,pos[maxn],dfstime;
ll val[maxn],C[maxn];
vector id[maxn],F[maxn],G[maxn];
vector V[maxn];
bool cmp(int a,int b)
{
return dfn[a]=0;i--)
{
int to=G[u][i];
dep[to]=dep[u]+1,dfs(to);
}
mx[u]=dfstime;
}
int lca(int u,int v)
{
if(dep[u]=0;i--) if(dep[fa[i][u]]>=dep[v]) u=fa[i][u];
if(u==v) return u;
for(int i=20;i>=0;i--) if(fa[i][u]!=fa[i][v]) u=fa[i][u],v=fa[i][v];
return fa[0][u];
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x,ll z)
{
// printf("[%d %lld]",x,z);
while(x<=n)
{
C[x]+=z;
x+=lowbit(x);
}
}
ll query(int x)
{
ll res=0;
while(x)
{
res+=C[x];
x-=lowbit(x);
}
return res;
}
int father(int x) { return (f[x]==x)?x:(f[x]=father(f[x])); }
bool cmpp(edge x,edge y)
{
return x.w=0;i--)
id[u].push_back(lca(id[u][i],id[u][i+1]));
sort(id[u].begin(),id[u].end(),cmp);
tot=unique(id[u].begin(),id[u].end())-id[u].begin();
while(id[u].size()>tot) id[u].pop_back();
F[u].clear();
int top=-1;
for(int i=0;i=0 && dfn[id[u][i]]>mx[id[u][sta[top]]]) top--;
F[u].push_back((top==-1)?-1:sta[top]),sta[++top]=i;
}
V[u].clear();
for(int i=0;i=0;i--)
{
if(id[u][i]<=tn)
pos[id[u][i]]=i,V[u][i]=max(V[u][i],val[id[u][i]]);
if(F[u][i]!=-1) V[u][F[u][i]]=max(V[u][F[u][i]],V[u][i]);
}
for(int i=0;i=0;i--)
if(w[i][u]<=v)
u=fa[i][u];
printf("%lld\n",query(mx[u])-query(dfn[u]-1));
}
else
{
// continue;
int i,z;
for(i=c[u],u=pos[u],z=V[i][u]+v;u!=-1 && z>V[i][u];u=F[i][u])
{
update(dfn[id[i][u]],z-V[i][u]);
if(F[i][u]!=-1)
update(dfn[id[i][F[i][u]]],V[i][u]-z);
V[i][u]=z;
}
}
}
}
return 0;
}
如果确定了一个点在水平线上,也就是一个米子和横
那么我们结合另一个点就能讨论得到三个中心位置,依次判断是否能射到所有的n个点即可
我们再讨论第一个点所在直线的四种情况,可以理解为坐标系旋转 45 ° 45\degree 45°
共计12次判断,时间复杂度 O ( 12 n ) O(12n) O(12n)
#include
using namespace std;
const int maxn=5e5+5;
int px[maxn],py[maxn];
int tx[maxn],ty[maxn];
int n;
bool ok(int x,int y)
{
return (!x || !y || x==y || x==-y);
}
bool check(int x,int y)
{
for(int i=1;i<=n;i++)
{
if(!ok(px[i]-x,py[i]-y))
return 0;
}
return 1;
}
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&tx[i],&ty[i]),px[i]=tx[i],py[i]=ty[i];
int now=1;
while(now
直接输出 n − m 2 \frac{n-m}{2} 2n−m即可
#include
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll qpow(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll inv2=qpow(2,mod-2);
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int T; scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
printf("%lld\n",1ll*(n-m)*inv2%mod);
}
return 0;
}
发现如果1个0,那么Alice必胜
如果有2个1/4个2/…,Alice必胜
如果有 2 i 2^i 2i个 i i i,相当于一个 i i i,看最后是否能凑够一个0即可
注意爆longlong的问题,倒着写会好一点
#include
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int c[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=0;i<=n;i++) scanf("%d",&c[i]);
ll gs=1;
int flag=0;
for(int i=0;i<=n;i++)
{
if(c[i]>=gs)
{
flag=1;
break;
}
gs-=c[i];
gs*=2;
if(gs>1000000) break;
}
if(flag) printf("Alice\n");
else printf("Bob\n");
}
return 0;
}