题目顺序不分难度
1005:用最小表示法求出每个串的最小字典序,然后用字符串哈希o1判断是不是相等即可
#include<bits/stdc++.h>
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(k<m&&s[I+k]==s[J+k]) k++;
if(k==m) break;
if(s[I+k]>s[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<m;j++)
a[i]=a[i]*mod+(s[k+j]-'a'+1);
}
int q;
scanf("%d", &q);
while(q--){
int x,y;
scanf("%d%d", &x, &y);
if(a[x]==a[y]){
cout<<"Yes\n";
}
else cout<<"No\n";
}
}
signed main(){
//cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t=1;
cin>>t;
while(t--) solve();
}
1009:每个组尽量平摊就行
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include <cmath>
#include<unordered_set>
#include <unordered_map>
#include <set>
#include<map>
using namespace std;
const int N=2e5+10,mod=1e9+7;
#define int long long
typedef long long LL;
int n,m;
void solve(){
int n,m,d;
cin>>n>>m>>d;
if((m+n-1ll)/n>=d) cout<<"Yes\n";
else cout<<"No\n";
}
signed main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t=1;
cin>>t;
while(t--) solve();
return 0;
}
1002:
一个经典的树形dp模型,求到每个点的最小代价
这里讲一下当复习了
f[u][2]表示当前点由自己看 儿子三个状态都能转移过来
f[u][1]表示儿子看守当前这个点,其中一个儿子看守即可
f[u][0]表示父亲看守这个点,儿子只能自己看或者他的孙子看儿子点了
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include <cmath>
#include<unordered_set>
#include <unordered_map>
#include <set>
#include<map>
using namespace std;
const int N=4e5+10,mod=1e9+7;
#define int long long
typedef long long LL;
int n,m;
int a[N];
vector<int> g[N];
int f[N][3];
void dfs(int u,int fa)
{
f[u][2] = a[u];
int sum = 0;
for (auto j:g[u])
{
if(j==fa) continue;
dfs(j,u);
f[u][0] += min(f[j][1], f[j][2]);
f[u][2] += min(min(f[j][0], f[j][1]), f[j][2]);
sum += min(f[j][1], f[j][2]);
}
f[u][1] = 1e18;
for (auto j:g[u])
{
f[u][1] = min(f[u][1], sum - min(f[j][1], f[j][2]) + f[j][2]);
}
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) f[i][0]=f[i][1]=f[i][2]=0;
for(int i=1;i<=n;i++) g[i].clear();
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<n;i++)
{
int u,v;cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,-1);
cout<<min(f[1][1],f[1][2])<<"\n";
}
signed main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t=1;
cin>>t;
while(t--) solve();
return 0;
}
1012:感觉这个题难在sg不在换根
首先只有一个点先手必输,当前状态是0,那么他父节点的sg函数是他儿子节点的+1
从博弈论出发父节点是当前全部儿子节点的sg函数的异或值
然后就换根地方了
从u到v?有啥变换 ,v的状态是全部儿子节点的异或,现在根变成v,u应该是v的儿子,
只要把这个变化量改了就行
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10,mod=1e9+7;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
int n,m;
int f[N],g[N];
vector<int> 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<n;i++){
int a,b;cin>>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<cstdio>
#include<algorithm>
#include<cmath>
#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]]<sz[nxt])son[now]=nxt;
}
Low[now]=tot_dfs;
}
void dfs_top(int now)
{
if(son[now]){
top[son[now]]=top[now];
dfs_top(son[now]);
}
for(int i=head[now];i;i=edge[i].nx){
int nxt=edge[i].to;
if(nxt==fa[now]||nxt==son[now])continue;
top[nxt]=nxt;
dfs_top(nxt);
}
}
int LCA(int a,int b){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])b=fa[top[b]];
else a=fa[top[a]];
}
return dep[a]<dep[b]?a:b;
}
bool In(int x,int y){
return Dfn[y]<=Dfn[x]&&Dfn[x]<=Low[y];
}
int mark[M];
struct Point{
int a,b;
};
Point Data[M][2];
int exgcd(int a,int b,int &x,int &y){
int d=a; if(b==0) x=1,y=0; else{
d=exgcd(b,a%b,y,x),y-=a/b*x;
}
return d;
}
int Get_ans(Point p1,Point p2){
int val=p2.b-p1.b;
val%=p2.a;
while(val<0)val+=p2.a;
while(val>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;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
Addedge(a,b);
Addedge(b,a);
}
dfs(1);
top[1]=1;
dfs_top(1);
for(int step=1;step<=m;step++){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
if(a==c){printf("%d\n",a);continue;}
int x1=LCA(a,b),x2=LCA(c,d);
if(dep[x1]>dep[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<ans_val){
ans_val=res;
ans=p;
}
}
if(p==x2)break;
p=fa[p];
}
p=d;
while(p!=x2){
Point p1=(Point){2*d2,d2-(dep[d]-dep[p])};
Point p2=(Point){2*d2,d2+(dep[d]-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<ans_val){
ans_val=res;
ans=p;
}
}
p=fa[p];
}
printf("%d\n",ans);
}
}
return 0;
}
1010:
首先找题目性质 Xi>=Xi-1
拆绝对值对于ai>x:ai=ai-x 不变
对于ai<x:
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<x:
维护 总和 sum2
维护 lazy1也是懒标记用来维护每个区间需要加的值,这个是叠加的k值,所以计算sum2的时候
还要加上后面那一坨的值
好吧可能很多人会懵逼....但是可能要自己理解吧
#include <bits/stdc++.h>
#define ll long long
#define Max 200005
using namespace std;
struct Tree{
ll lazy1,lazy2,lazy3,cnt,minn,sum1,sum2;
}st[Max*4];
int T,n,m,a[Max];
inline Tree up(Tree ls,Tree rs){
Tree ans;
ans.lazy1=ans.lazy3=0;ans.lazy2=1;
ans.minn=min(ls.minn,rs.minn);
ans.sum1=ls.sum1+rs.sum1;
ans.sum2=ls.sum2+rs.sum2;
ans.cnt=ls.cnt+rs.cnt;
return ans;
}
inline void down(int node,int L,int R){
int ls=node<<1,rs=node<<1|1;
int mid=(L+R)>>1;
st[ls].lazy3+=st[node].lazy3;
st[rs].lazy3+=st[node].lazy3;
st[ls].sum1-=st[node].lazy3*st[ls].cnt;
st[rs].sum1-=st[node].lazy3*st[rs].cnt;
st[ls].minn-=st[node].lazy3;
st[rs].minn-=st[node].lazy3;
st[ls].lazy1=st[node].lazy1+st[ls].lazy1*st[node].lazy2;
st[rs].lazy1=st[node].lazy1+st[rs].lazy1*st[node].lazy2;
st[ls].lazy2*=st[node].lazy2;
st[rs].lazy2*=st[node].lazy2;
st[ls].sum2=st[node].lazy1*(mid-L+1-st[ls].cnt)+st[ls].sum2*st[node].lazy2;
st[rs].sum2=st[node].lazy1*(R-mid-st[rs].cnt)+st[rs].sum2*st[node].lazy2;
st[node].lazy1=st[node].lazy3=0;
st[node].lazy2=1;
return;
}
inline void build(int node,int L,int R){
if(L==R){
st[node].lazy1=st[node].lazy3=0;
st[node].lazy2=1;
st[node].minn=st[node].sum1=a[L];
st[node].sum2=0;
st[node].cnt=1;
return;
}
int mid=(L+R)>>1;
build(node<<1,L,mid);
build(node<<1|1,mid+1,R);
st[node]=up(st[node<<1],st[node<<1|1]);
return;
}
inline void change(int node,int l,int r,int L,int R,int k){
if(L>=l&&R<=r){
if(st[node].cnt){
if(L==R){
if(st[node].sum1<k){
st[node].sum2=k-st[node].sum1;
st[node].sum1=st[node].cnt=0;
st[node].minn=1e18;
}else{
st[node].minn=st[node].sum1=st[node].sum1-k;
}
}else{
if(st[node].minn<k){
down(node,L,R);
int mid=(L+R)>>1;
change(node<<1,l,r,L,mid,k);
change(node<<1|1,l,r,mid+1,R,k);
st[node]=up(st[node<<1],st[node<<1|1]);
}else{
st[node].lazy3+=k;
st[node].minn-=k;
st[node].sum1-=1ll*k*st[node].cnt;
st[node].lazy1=k-st[node].lazy1;
st[node].lazy2*=-1;
st[node].sum2=1ll*k*(R-L+1-st[node].cnt)-st[node].sum2;
}
}
}else{
st[node].lazy1=k-st[node].lazy1;
st[node].lazy2*=-1;
st[node].sum2=1ll*k*(R-L+1)-st[node].sum2;
}
// cout<<L<<" "<<R<<" "<<st[node].sum1<<" "<<st[node].sum2<<endl;
return;
}
down(node,L,R);
int mid=(L+R)>>1;
if(l<=mid)change(node<<1,l,r,L,mid,k);
if(r>mid)change(node<<1|1,l,r,mid+1,R,k);
st[node]=up(st[node<<1],st[node<<1|1]);
return;
}
inline Tree query(int node,int l,int r,int L,int R){
if(L>=l&&R<=r)return st[node];
down(node,L,R);
int mid=(L+R)>>1;
if(r<=mid)return query(node<<1,l,r,L,mid);
if(l>mid)return query(node<<1|1,l,r,mid+1,R);
return up(query(node<<1,l,r,L,mid),query(node<<1|1,l,r,mid+1,R));
}
int main(){
freopen("data3.in","r",stdin);
freopen("data3.out","w",stdout);
ios::sync_with_stdio(false);
cin>>T;
while(T--){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
for(int i=1;i<=m;i++){
int opt;
cin>>opt;
if(opt==1){
int l,r,x;
cin>>l>>r>>x;
change(1,l,r,1,n,x);
}else{
int l,r;
cin>>l>>r;
Tree ans=query(1,l,r,1,n);
cout<<ans.sum1+ans.sum2<<'\n';
}
}
}
return 0;
}
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<bits/stdc++.h>
using namespace std;
const int N = 510;
long long INF=1e18;
long long f[110][110][N];
long long val[N],a[N];
int n,maxl,m,P;
int Base[N];
long long get(int x){
int level=(x-1)/m,b=(x-1)%m+1;
return 1ll*Base[level]*val[b];
}
long long dfs(int l,int r,int p){
if(l>r){
if(p==0) return 0;
else return -INF;
}
if(l==r){
if(p==0) return val[a[l]];
if(p==a[l]) return 0;
return -INF;
}
if(f[l][r][p]!=-1) return f[l][r][p];
long long res=-INF;
if(p==0){//如果没要求
//1.可以直接分开两个区间
for(int mid=l;mid<r;mid++){
long long left=dfs(l,mid,0);
long long right=dfs(mid+1,r,0);
if(left!=-INF&&right!=-INF)
res=max(res,left+right);
}
//2.最后打出得牌种类是 i
for(int i=1;i<=maxl*m;i++)
{
long long tmp=dfs(l,r,i);
if(tmp==-INF) continue;
res=max(res,tmp+get(i));
}
}
else if(p<=m){
//如果最后打出得牌等级是1
for(int i=l;i<=r;i++)
{
if(a[i]==p){
long long left=dfs(l,i-1,0);
long long right=dfs(i+1,r,0);
if(left!=-INF&&right!=-INF){
res=max(res,left+right);
}
}
}
}
else{
//最后打出的牌不是1级,我们需要找两个区间合并两张牌成这种高级牌
for(int i=l;i<r;i++){
long long left=dfs(l,i,p-m);
long long right=dfs(i+1,r,p-m);
res=max(res,left+right);
}
}
return f[l][r][p]=res;
}
void solve(){
memset(f,-1,sizeof(f));
cin>>n>>m>>maxl>>P;
Base[0]=1;
for(int i=1;i<=maxl;i++)
Base[i]=1ll*Base[i-1]*P;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>val[i];
cout<<dfs(1,n,0)<<"\n";
}
signed main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t;
cin>>t;
while(t--) solve();
}