String
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<bits/stdc++.h>
using namespace std;
#define far(i,t,n) for(int i=t;i<n;++i)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
char c[100010];//原数组
char ans[100010];//答案字符串
int k;
int L[26],R[26];
int cnt[100010][30],used[30];//cnt记录当前下标及其后的字符串中各种字母的个数,used记录每个字母已经选了几个
queue<int>q[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<res)
return false;
return true;
}
int main()
{
while(~scanf("%s%d",c+1,&k))
{
far(i,0,26)
while(!q[i].empty())
q[i].pop();
memset(cnt,0,sizeof(cnt));
memset(used,0,sizeof(used));
ans[1]=0;
far(i,0,26)
scanf("%d%d",&L[i],&R[i]);
int l=strlen(c+1);
far(i,1,l+1)
q[c[i]-'a'].push(i);
for(int i=l;i>=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<k)
{
nowCnt++;
flag=0;
far(i,0,26)
{
while(q[i].size()&&q[i].front()<=lastidx)
q[i].pop();
if(used[i]<R[i]&&q[i].size())
{
used[i]++;
if(check(nowCnt,q[i].front()+1))
{
lastidx=q[i].front();
ans[nowCnt]=c[q[i].front()];
ans[nowCnt+1]=0;
q[i].pop();
flag=1;
break;
}
used[i]--;
}
}
if(!flag)
{
success=0;
break;
}
}
ans[k + 1] = '\0';
if(success)
printf("%s\n",ans+1);
else
puts("-1");
}
}
Blank
theme:给定一个大小为n的数组,用0,1,2,3来填充数组,给定m个约束条件,其中第i个条件l,r,x表示数组的区间[l,r]之间不同元素个数为x,求有多少种填充方案?1≤n≤100,1≤m≤100,mod 998244353
Operation
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<bits/stdc++.h>
#define far(i,s,n) for(int i=s;i<n;++i)
typedef long long ll;
using namespace std;
int b[1000010][35],pos[1000010][35];
void Insert(int x,int i)
{
int now=i;
for(int j=30;j>=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;
}
}
Vacation
theme:给定n辆车头离停车线的距离s,车长l,最大速度v,按s从大到小给,要求不能超车行驶(最多车头顶着车尾行驶),问第一辆车(距离最远的那辆)抵达停车线花费的最少时间?1≤n≤10^5
solution:两种考虑方式:1、对于处在最后的车,如果前面有车速度比它小,且比它们中间的车速度都小,则很有可能在它抵达停车线前被堵住了,这时它们都得按堵住的最前面的车的速度行驶,(注意即使这辆车通过了停车线,后面的车还是不能超过他),所以最后一辆车通过停车线的时间相当于堵住它的车x到达停车线的时间+最后一辆车以x的速度行驶它与x间所有车长的时间。所以我们可以枚举每一辆车为堵住最后一辆车的,算出它行驶到停车线的时间+以它的速度行驶它与最后一辆车之间车的车长之和的时间,取最大值即为答案。
#include<bits/stdc++.h>
#define far(i,s,n) for(int i=s;i<n;++i)
typedef long long ll;
using namespace std;
ll s[100010],l[100010],v[100010];
ll suml[100010];
int main()
{
int n;
while(~scanf("%d",&n))
{
far(i,0,n+1)
scanf("%lld",&l[i]);
far(i,0,n+1)
scanf("%lld",&s[i]);
far(i,0,n+1)
scanf("%lld",&v[i]);
suml[0]=0;
far(i,1,n+1)
suml[i]=suml[i-1]+l[i];
double ans=0;
far(i,0,n+1)
ans=max(ans,1.0*(suml[i]+s[i])/v[i]);
printf("%f\n",ans);
}
}
Path
theme:给定一个有向图,可以有重边,每条边上有一个权值表示删掉这条边的代价,问最少花费多少代价能使从s到t节点的最短路径增大?1≤n,m≤10000
solution:先找出从s到t的所有最短路径所经过的边,先跑一遍最短路,如果一条边的起始与终止节点u与v满足d[u]+w=d[v],说明这条边在某条最短路上(重边也一样)。得到这些边形成的新图,则问题可以转化为在新图上求最小割,最大流=最小割,跑一遍dinic.
dijkstra时间复杂度为o(VE)
#include <bits/stdc++.h>
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> 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 <Node> 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 <int> 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 <int> 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;
}