题目顺序不分难度
1005:用最小表示法求出每个串的最小字典序,然后用字符串哈希o1判断是不是相等即可
#include
using namespace std;
const int N = 2e5+10,mod=131;
typedef unsigned long long ULL;
int n,m;
ULL a[N];
char s[N];
void solve(){
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++)
{
scanf("%s", s+1);
for(int j=m+1;j<=m+m;j++)
s[j]=s[j-m];
int I=1,J=2;
while(J<=m&&I<=m){
int k=0;
while(ks[J+k]) I+=k+1;
else J+=k+1;
if(I==J) J++;
}
int k=min(I,J);
a[i]=0;
for(int j=0;j>t;
while(t--) solve();
}
1009:每个组尽量平摊就行
#include
#include
#include
#include
#include
#include
#include
#include
#include
1002:
一个经典的树形dp模型,求到每个点的最小代价
这里讲一下当复习了
f[u][2]表示当前点由自己看 儿子三个状态都能转移过来
f[u][1]表示儿子看守当前这个点,其中一个儿子看守即可
f[u][0]表示父亲看守这个点,儿子只能自己看或者他的孙子看儿子点了
#include
#include
#include
#include
#include
#include
#include
#include
#include
1012:感觉这个题难在sg不在换根
首先只有一个点先手必输,当前状态是0,那么他父节点的sg函数是他儿子节点的+1
从博弈论出发父节点是当前全部儿子节点的sg函数的异或值
然后就换根地方了
从u到v?有啥变换 ,v的状态是全部儿子节点的异或,现在根变成v,u应该是v的儿子,
只要把这个变化量改了就行
#include
using namespace std;
const int N = 2e5+10,mod=1e9+7;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair PII;
int n,m;
int f[N],g[N];
vector G[N];
int qmi(int a, int k, int p) // 求a^k mod p
{
int res = 1 % p;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
void dfs1(int u,int fa)
{
f[u]=g[u]=0;
for(auto v:G[u]){
if(v==fa) continue;
dfs1(v,u);
f[u]^=(f[v]+1);
}
}
void dfs2(int u,int fa){
for(auto v:G[u]){
if(v==fa) continue;
g[v]=f[v]^((g[u]^(f[v]+1))+1);
dfs2(v,u);
}
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i>a>>b;
G[a].push_back(b);
G[b].push_back(a);
}
dfs1(1,1);
g[1]=f[1];
dfs2(1,1);
int cnt=0;
for(int i=1;i<=n;i++) if(g[i]) cnt++;
cout<<1ll*cnt*qmi(n,mod-2,mod)%mod<<"\n";
}
signed main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t=1;
cin>>t;
while(t--) solve();
}
1001:
首先n点n-1条边还是连通图,是个树(好吧其实好像说了等于没说)
这个题其实就是一眼跟LCA有关的题,只是他要分析
第一个人的起点终点是sa,ta 第二个是sb tb
首先分析一下假设在x点相遇
第一个人在x点的时间可能是两个去终点的路中到x点
距离等于 k1*2*dist(sa,ta)+dist(sa,x) (就是不知道第几个来回的路上,起点到x点的路径)
也可能是终点回起点中到x点
距离等于 k1*2*dist(sa,ta)+dist(sa,x)+dist(sa,ta)
第二个人同理
k2*2*dist(sb,tb)+dist(sb,x)
k2*2*dist(sb,tb)+dist(sb,x)+dist(sb,tb)
相遇就是时间一样嘛0.0
联立方程求最小正整数解,用扩展欧几里得
我这里举例把
k1*2*dist(sa,ta)+dist(sa,x) =k2*2*dist(sb,tb)+dist(sb,x) 要他们相等
(dist都是已经知道的,可以通过lca,dist[a]+dist[b]-2*dist(lca(a,b)))
把2*dist(sa,ta)=a,2*dist(sb,tb)-b
方程等于 k1*a-k2*b=dist(sb,x)-dist(sa-x)
设dist(sb,x)-dist(sa-x)=d
k1*a-k2*b=d
设c=-b
k1*a+k2*c=d
根据贝祖定理
d%gcd(a,c)能整除才有解
然后你就直接用一个扩展欧几里得求出k1,k2即可
然后记得k1,k2别忘了乘d/gcd(a,c)倍
然后还有个问题,他要正整数解法
如果是负数咋办,你要减少k2那里,扩大k
正数就扩大k2(k2那一坨是负数有符号),可以把k1减少,别变成负数就行
p=b/d,q=a/d,是因为(b,d)的最小公倍数是d
你要保持等式成立,只能加减共同的数
比如k1>=0
你直接把k1变成最小比如1?
(k1-1)/p,就是能保持符合等式且不会变成负数
好了可能有人看不懂...但是问题不大因为就算会了这题代码量也很大
代码我直接给题解的了...但是我还是不知道为啥他要用树剖不用倍增
#include
#include
#include
#define M 5005
using namespace std;
struct E{
int to,nx;
}edge[M<<1];
int tot,head[M];
void Addedge(int a,int b){
edge[++tot].to=b;
edge[tot].nx=head[a];
head[a]=tot;
}
int sz[M],son[M],top[M],dep[M];
int Dfn[M],Low[M],tot_dfs;
int fa[M];
void dfs(int now){
Dfn[now]=++tot_dfs;
sz[now]=1;son[now]=0;
for(int i=head[now];i;i=edge[i].nx){
int nxt=edge[i].to;
if(nxt==fa[now])continue;
fa[nxt]=now;
dep[nxt]=dep[now]+1;
dfs(nxt);
sz[now]+=sz[nxt];
if(sz[son[now]]p2.a)val-=p2.a;
int a=p1.a,b=-p2.a;
int x,y,d=exgcd(a,b,x,y);
if(val%d!=0)return 1e9;
x*=val/d;y*=val/d;
int p=b/d,q=a/d;
if(x<0){
int k=ceil((1.0-x)/p);
x+=p*k,y-=q*k;
}else if(x>=0){
int k=(x-1)/p;
x-=p*k,y+=q*k;
}
return a*x+p1.b;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,m;
tot=0;
scanf("%d%d",&n,&m);
tot_dfs=0;
for(int i=1;i<=n;i++)head[i]=mark[i]=0;
for(int i=1;idep[x2]){
swap(a,c);
swap(b,d);
swap(x1,x2);
}
if(!In(a,x2)&&!In(b,x2)){
puts("-1");
continue;
}
int d1=dep[a]+dep[b]-2*dep[x1],d2=dep[c]+dep[d]-2*dep[x2];
int p=a;
while(1){
Data[p][0]=(Point){2*d1,dep[a]-dep[p]};
Data[p][1]=(Point){2*d1,2*d1-(dep[a]-dep[p])};
mark[p]=step;
if(p==x1)break;
p=fa[p];
}
p=b;
while(p!=x1){
Data[p][0]=(Point){2*d1,d1-(dep[b]-dep[p])};
Data[p][1]=(Point){2*d1,d1+(dep[b]-dep[p])};
mark[p]=step;
p=fa[p];
}
int ans_val=1e9,ans=-1;
p=c;
while(1){
Point p1=(Point){2*d2,dep[c]-dep[p]};
Point p2=(Point){2*d2,2*d2-(dep[c]-dep[p])};
if(mark[p]==step){
int res=1e9;
res=min(min(Get_ans(p1,Data[p][0]),Get_ans(p1,Data[p][1])),min(Get_ans(p2,Data[p][0]),Get_ans(p2,Data[p][1])));
if(res
1010:
首先找题目性质 Xi>=Xi-1
拆绝对值对于ai>x:ai=ai-x 不变
对于ai 1.ai=x1-ai 2.ai=x2-x1+ai 3.ai=x3-x2+x1-ai 且由于第一个性质,ai会一直小于X,可以发现每多一个数x,会改变ai的正负(ai=x2-x1+ai,不单单只表示ai,是除了X后面的那一堆) 所以考虑分开两个情况来维护 对于ai>=x: 维护总和 sum1 维护区间有多少个ai>=x的个数 cnt 维护懒标记 lazy3用来当前X需要减多少 维护当前区间最小值 对于共同维护 lazy2来维护当前数是加还是减(这个是不确定的要维护,比如对于两个相交的区间,不相交的地方可能是减法,但是相交由于两次减,会变成加这个区间的值) 对于ai 维护 总和 sum2 维护 lazy1也是懒标记用来维护每个区间需要加的值,这个是叠加的k值,所以计算sum2的时候 还要加上后面那一坨的值 好吧可能很多人会懵逼....但是可能要自己理解吧 1003: 挺明显的区间dp吧 考虑最后一张牌出的类型 这里先说一下类型有m张牌 level个等级 所以类型有 m*level个不同状态 我们让状态为0是表示什么牌都没打出 f[l][r][p]表示区间【l,r】中最后打出牌的类型是p 考虑转移如果区间长度只有一个且什么牌都没打出(即状态为0),那么只能打出这唯一一张牌,贡献为a[l] 如果p不为0,即表示当最后牌打出的是p状态得牌贡献为0 对于一般区间 如果什么牌都没出: 1.分成两个不同区间 2.枚举最后打出的牌是1到m*level得牌 如果已经确定了最后打出什么牌 1.如果打出的牌最后等级为1,那么枚举区间中状态是p得牌,然后打出中间这一张牌,加上左右两个区间 2.如果打出的牌等级是P,那么你需要枚举分割点从两个区间获得最后打出的牌是等级比P小的且值是相同得两张牌#include
#include