theme:给定一个仅包含小写字母的字符串,求字典序最小的长度为k的子序列满足26个约束条件:第i个约束条件表示字母i出现的次数在[l,r]范围内。|S|≤10^5
solution:最终是要求字典序最大的序列,所以贪心选择,每选一个元素都从a~z顺序选择。所以首先用queue(因为是从前往后弹出,即先进先出)记录下每一个每个字母在数组中的下标,对每一个字母所在的所有位置(但要在上一个被选位置下标之后)检查选了它之后其后元素能否满足条件,检查时检查两方面:设还要选res=k-nowCnt个 1、选res能否把所有还没有L[i]个的字母填满
2、把所有元素都填到R[i]个了是否res为0.
#include
using namespace std;
#define far(i,t,n) for(int i=t;iq[26];
bool check(int nowCnt,int idx)//判定第nowCnt个字符选中后,其后的序列能否满足要求
{
int res=k-nowCnt;
int suml=0;
far(i,0,26)//判断其后序列能否填到每个L[i]
{
if(L[i]-used[i]>cnt[idx][i])
return false;
if(L[i]-used[i]>0)
suml+=L[i]-used[i];
}
if(suml>res)
return false;
int sumr=0;
far(i,0,26)//判定要选k个元素会不会有的字母超过R[i]个
sumr+=min(R[i]-used[i],cnt[idx][i]);
if(sumr=1;--i)
far(j,0,26)
cnt[i][j]=cnt[i+1][j]+((c[i]-'a')==j);
int nowCnt=0,lastidx=0;//nowCnt记录已选的个数,lastidx记录上一个被选元素的下标
int flag=0,success=1;//flag标识第nowCnt个元素能否选出
while(nowCnt
theme:给定一个大小为n的数组,用0,1,2,3来填充数组,给定m个约束条件,其中第i个条件l,r,x表示数组的区间[l,r]之间不同元素个数为x,求有多少种填充方案?1≤n≤100,1≤m≤100,mod 998244353
theme:给定长度为n的数组,有m次操作,操作类型有两种:
0 l r:表示询问从区间[l,r]中任意选出若干元素能得到的最大抑或和值
1 x:表示在数组最后插入一个元素x.
1≤n≤5×10^5,1≤m≤5×10^5
solution:线性基。不过因为查询的是[l,r]所以要记录一下每个被插入线性基中元素的下标。
#include
#define far(i,s,n) for(int i=s;i=0;--j)
{
b[i][j]=b[i-1][j];
pos[i][j]=pos[i-1][j];
}
for(int j=30;j>=0;--j)
{
if(x>>j)
{
if(!b[i][j])
{
b[i][j]=x;
pos[i][j]=now;
return;
}
else
{
if(now>pos[i][j])
{
swap(pos[i][j],now);
swap(x,b[i][j]);
}
x^=b[i][j];
}
}
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
far(i,1,n+1)
{
int a;
scanf("%d",&a);
Insert(a,i);
}
int pre=0;
far(i,0,m)
{
int opt;
scanf("%d",&opt);
if(opt==0)
{
int l,r;
scanf("%d%d",&l,&r);
l=(l^pre)%n+1,r=(r^pre)%n+1;
if(l>r)
swap(l,r);
int ans=0;
for(int j=30;j>=0;--j)
if(pos[r][j]>=l)
ans=max(ans,ans^b[r][j]);
pre=ans;
printf("%d\n",ans);
}
else
{
int x;
scanf("%d",&x);
x=x^pre;
++n;
Insert(x,n);
}
}
for (int i=1;i<=n;++i)
for (int j=30;j>=0;--j)
b[i][j]=pos[i][j]=0;
}
}
theme:给定n辆车头离停车线的距离s,车长l,最大速度v,按s从大到小给,要求不能超车行驶(最多车头顶着车尾行驶),问第一辆车(距离最远的那辆)抵达停车线花费的最少时间?1≤n≤10^5
solution:两种考虑方式:1、对于处在最后的车,如果前面有车速度比它小,且比它们中间的车速度都小,则很有可能在它抵达停车线前被堵住了,这时它们都得按堵住的最前面的车的速度行驶,(注意即使这辆车通过了停车线,后面的车还是不能超过他),所以最后一辆车通过停车线的时间相当于堵住它的车x到达停车线的时间+最后一辆车以x的速度行驶它与x间所有车长的时间。所以我们可以枚举每一辆车为堵住最后一辆车的,算出它行驶到停车线的时间+以它的速度行驶它与最后一辆车之间车的车长之和的时间,取最大值即为答案。
#include
#define far(i,s,n) for(int i=s;i
theme:给定一个有向图,可以有重边,每条边上有一个权值表示删掉这条边的代价,问最少花费多少代价能使从s到t节点的最短路径增大?1≤n,m≤10000
solution:先找出从s到t的所有最短路径所经过的边,先跑一遍最短路,如果一条边的起始与终止节点u与v满足d[u]+w=d[v],说明这条边在某条最短路上(重边也一样)。得到这些边形成的新图,则问题可以转化为在新图上求最小割,最大流=最小割,跑一遍dinic.
dijkstra时间复杂度为o(VE)
#include
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int maxn = 10010;
const int maxm = 20020;
ll dist1[maxn];
bool vis[maxn];
int n, m, N;
struct Node
{
int v;
ll c;
Node(int _v, ll _c): v(_v), c(_c) {}
bool operator < (const Node & r) const
{
return c > r.c;
}
};
struct Edge//最短路
{
int to;
ll cost;
Edge(int _to, ll _cost): to(_to), cost(_cost) {}
};
vector edge[maxn];
void addedge(int u, int v, ll w) // 最短路加边
{
edge[u].push_back(Edge(v, w));
}
void dijkstra(int s, ll dist[]) // 最短路
{
for (int i = 1; i <= N; ++i)
{
dist[i] = inf;
vis[i] = false;
}
dist[s] = 0;
priority_queue Q;
Q.push(Node(s, dist[s]));
while (!Q.empty())
{
Node tmp = Q.top();
Q.pop();
int u = tmp.v;
if (vis[u])
continue;
vis[u] = true;
for (int i = 0; i < edge[u].size(); ++i)
{
int v = edge[u][i].to;
ll cost = edge[u][i].cost;
if (!vis[v] && dist[v] > dist[u] + cost)
{
dist[v] = dist[u] + cost;
Q.push(Node(v, dist[v]));
}
}
}
}
struct Edges
{
int u, v;
ll cap;
Edges() {}
Edges(int u, int v,ll cap): u(u), v(v), cap(cap) {}
} es[maxm];
int R, S, T;//边下标、源点、汇点
vector tab[maxn]; // 边集
ll dis[maxn];
int current[maxn];
void addedges(int u, int v, ll cap) // 最大流加边
{
tab[u].push_back(R);
es[R++] = Edges(u, v, cap); // 正向边
tab[v].push_back(R);
es[R++] = Edges(v, u, 0); // 反向边容量为0
// 正向边下标通过异或就得到反向边下标, 2 ^ 1 == 3 ; 3 ^ 1 == 2
}
int BFS()
{
queue q;
q.push(S);
memset(dis, 0x3f3f, sizeof(dis));
dis[S] = 0;
while (!q.empty())
{
int h = q.front();
q.pop();
for (int i = 0; i < tab[h].size(); i++)
{
Edges &e = es[tab[h][i]];
if (e.cap > 0 && dis[e.v] == inf)
{
dis[e.v] = dis[h] + 1;
q.push(e.v);
}
}
}
return dis[T] < inf; // 返回是否能够到达汇点
}
ll dinic(int x, ll maxflow)
{
if (x == T)
return maxflow;
// i = current[x] 当前弧优化
for (int i = current[x]; i < tab[x].size(); i++)
{
current[x] = i;
Edges &e = es[tab[x][i]];
if (dis[e.v] == dis[x] + 1 && e.cap > 0)
{
ll flow = dinic(e.v, min(maxflow, e.cap));
if (flow)
{
e.cap -= flow; // 正向边流量降低
es[tab[x][i] ^ 1].cap += flow; // 反向边流量增加
return flow;
}
}
}
return 0; // 找不到增广路 退出
}
ll DINIC()
{
ll ans = 0;
while (BFS()) // 建立分层图
{
ll flow;
memset(current, 0, sizeof(current)); // BFS后应当清空当前弧数组
while (flow = dinic(S, inf)) // 一次BFS可以进行多次增广
ans += flow;
}
return ans;
}
int a[maxm], b[maxm];
ll c[maxm];
int main()
{
int kase;
scanf("%d", &kase);
while (kase--)
{
scanf("%d%d", &n, &m);
N = n;
for (int i = 0; i <= N; ++i) // 清空
edge[i].clear();
for (int i = 1; i <= m; ++i) // 先正向加边
{
scanf("%d%d%lld", &a[i], &b[i], &c[i]);
addedge(a[i], b[i], c[i]);
}
S=1,T=n;
dijkstra(S, dist1); // 跑S到所有点的最短路
R = 0;
for (int i = 0; i <= N; i++)
tab[i].clear();
for (int i = 1; i <= m; ++i)//建新图用于最大流
{
if (a[i] != b[i] && dist1[a[i]] + c[i] == dist1[b[i]]) // 最短路上的边
addedges(a[i], b[i], c[i]);
}
ll ans = DINIC(); // 求最大流
printf("%lld\n", ans);
}
return 0;
}