2022 上海市赛vp
A(暴力)
暴力枚举所有可能的等式,按照要求check
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
const int Maxn=100005;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
char eq[10],tag[10],now[10];
int num[20],tmp[20],tt[20],pnum[20];
vector<string> ans;
int check()
{
rep(i,0,10) tmp[i]=0;
rep(i,1,8) if(i!=3&&i!=6)
{
if(eq[i]==now[i]&&tag[i]!='G') return 0;
if(eq[i]!=now[i]&&tag[i]=='G') return 0;
if(eq[i]!=now[i]) tmp[now[i]-'0']++;
}
rep(i,0,10)
if(tt[i]&&pnum[i]!=tmp[i]) return 0;
else if(!tt[i]&&pnum[i]>tmp[i]) return 0;
return 1;
}
int main()
{
scanf("%s%s",eq+1,tag+1);
now[3]='+',now[6]='=';
rep(i,1,8) if(i!=3&&i!=6&&tag[i]!='G') num[eq[i]-'0']++;
rep(i,1,8) if(i!=3&&i!=6)
{
if(tag[i]=='P') pnum[eq[i]-'0']++;
else if(tag[i]=='B') tt[eq[i]-'0']=1;
}
rep(i,0,99) rep(j,0,99-i)
{
now[1]='0'+i/10,now[2]='0'+i%10;
now[4]='0'+j/10,now[5]='0'+j%10;
now[7]='0'+(i+j)/10,now[8]='0'+(i+j)%10;
if(check()) ans.push_back(string(now+1));
}
printf("%d\n",(int)ans.size());
for(auto s:ans) cout<<s<<'\n';
}
B(差分约束、带权并查集)
考虑使用差分约束,令左括号前缀和数组为\(s_i\),有约束\(0\le s_i-s_{i-1}\le 1\)与\(s_i-(i-s_i)\ge 0\),同时题目中给出若干等号约束。总约束为\(O(n+m)\)级别,判负环复杂度太高
而等号约束显然有大量冗余,利用带权并查集解决即可
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int Maxn=100005;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n,m,fa[3010],d[3010];
int find(int x)
{
if(x==fa[x]) return x;
int f=find(fa[x]);
d[x]+=d[fa[x]];
return fa[x]=f;
}
int merge(int x,int y,int w)
{
int f1=find(x),f2=find(y);
if(f1==f2){if(d[x]!=d[y]+w) return -1;return 0;}
fa[f2]=f1,d[f2]=d[x]-d[y]-w;
return 1;
}
// (u,v,w) -> d[v]-d[u]<=w
vector<pii> G[3010];
int inq[3010],vis[3010],dis[3010];
queue<int> q;
void ins(int u,int v,int w)
{
G[u].emplace_back(v,w);
G[v].emplace_back(u,-w);
}
int main()
{
n=read(),m=read();
if(n&1) return puts("?"),0;
rep(i,1,n) fa[i]=i;
rep(i,1,m)
{
int l=read(),r=read(),w=read()+(r-l+1);
if(w&1) return puts("?"),0;
w>>=1;
if(w<0||w>r-l+1) return puts("?"),0;
int ret=merge(r,l-1,w);
if(ret<0) return puts("?"),0;
else if(ret>0) ins(l-1,r,w);
}
ins(0,n,n/2);
rep(i,1,n)
{
G[i].emplace_back(i-1,0);
G[i-1].emplace_back(i,1);
G[i].emplace_back(0,-(i+1)/2);
}
rep(i,0,n) q.push(i),vis[i]++,inq[i]=1;
while(!q.empty())
{
int x=q.front();
q.pop();inq[x]=0;
for(auto [v,w]:G[x]) if(dis[v]>dis[x]+w)
{
dis[v]=dis[x]+w;
if(!inq[v]) inq[v]=1,q.push(v),vis[v]++;
if(vis[v]>=n+1) return puts("?"),0;
}
}
printf("! ");
rep(i,1,n) putchar(dis[i]>dis[i-1]?'(':')');
}
C(二次函数)
喝咖啡满足两个性质:
- 开始喝咖啡的体力值\(x\)满足\(x\le \frac{C(C+1)}{2}\)
- 当某一时刻开始喝咖啡后,后续喝咖啡不间断
考虑枚举最后一次喝咖啡的体力值\(x,1\le x\le C+1\),则第一次喝咖啡的体力值为\(x+k(C+1),k=\lfloor\frac{\min(\frac{C(C+1)}{2},S)-x}{C+1}\rfloor\),根据该式可以发现\(k\)的取值至多为两种,则问题可以转化为对给定\(k,l,r\),求\(x\in[l,r]\)使得答案最大
贡献为:
整理后得到极值点为\(\frac{2c-2k-1}{2}\)即整数极值点为\(c-k,\ c-k-1\),分别判断是否在范围内并与边界点取max
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
const int Maxn=100005;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
ll s,c,lim;
inline ll s1(ll l,ll r){return (l+r)*(r-l+1)/2;}
inline ll getf(ll x,ll k)
{
return (k+1)*x*c+s1(x+k*(c+1)+1,s);
}
ll calc(ll l,ll r,ll k)
{
if(l>r) return 0;
ll bas=(c*c+c)*s1(1,k),res=max(getf(l,k),getf(r,k));
ll mid=c-k;
if((l<=mid&&mid<=r)||(l<=mid-1&&mid-1<=r)) res=max(res,getf(mid,k));
return res+bas;
}
void solve()
{
s=read(),c=read(),lim=min(s1(1,c),s);
ll y=lim-lim/(c+1)*(c+1);
printf("%lld\n",max(calc(1,y,lim/(c+1)),calc(y+1,c+1,lim/(c+1)-1)));
}
int main()
{
rep(T,1,read()) solve();
}
E(dp)
设\(f[i][j]\)表示串\(S\)配到\(i\),串\(F\)匹配到\(j\),首次匹配在\(S\)中最靠后的位置
\(O(nm)\)暴力转移即可
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
const int MAXN=100100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n,m,f[MAXN][110],ans,al,ar;
char sa[MAXN],sb[110];
void solve()
{
scanf("%s%s",sa+1,sb+1);
n=strlen(sa+1),m=strlen(sb+1);
ans=inf;
rep(i,1,m) f[0][i]=-inf;
rep(i,1,n) rep(j,1,m)
{
f[i][j]=-inf;
if(sa[i]==sb[j])
{
if(j==1) f[i][j]=i;
else f[i][j]=f[i-1][j-1];
}
else f[i][j]=f[i-1][j];
if(j==m&&(i-f[i][j]+1)<ans) ans=i-f[i][j]+1,al=f[i][j],ar=i;
}
rep(i,al,ar) putchar(sa[i]);puts("");
}
int main()
{
rep(T,1,read()) solve();
}
G(签到)
简单分类讨论
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int T;
ll B, R, D, S;
scanf("%d", &T);
while (T--)
{
scanf("%lld%lld%lld%lld", &B, &R, &D, &S);
if (R == 0)
{
puts(D == 0 ? "ok" : "gua!");
}
else
{
ll t = (S*R) / 60 + 1;
puts(t * B >= D ? "ok" : "gua!");
}
}
return 0;
}
H(简单观察)
最后一定存在一段长度大于等于k的区间,否则无解
利用这个区间倒着模拟一定可以成功染色,每一段区间的贡献是独立的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define fi first
#define se second
int c[1000011], n, m, K;
int len; pii a[1000011];
int main()
{
int T, i;
scanf("%d", &T);
while (T--)
{
scanf("%d%d%d", &n, &m, &K);
for (i=1; i<=n; ++i)
scanf("%d", &c[i]);
len=0;
for (i=1; i<=n; ++i)
{
if (!len || c[i]!=c[i-1])
{
++len;
a[len].fi = c[i];
a[len].se = 1;
}
else
{
++a[len].se;
}
}
if (len > 1 && a[1].fi == a[len].fi)
{
a[1].se += a[len].se;
--len;
}
int st = -1;
for (i=1; i<=len; ++i)
if (a[i].se >= K)
{
st = i;
break;
}
if (st == -1)
{
puts("-1");
continue;
}
int ans = 0;
for (i=1; i<=len; ++i)
{
ans += (a[i].se - 1) / K + 1;
}
printf("%d\n", ans);
}
return 0;
}
I(期望)
考虑预处理所有的状态与转移,求期望是经典的有向图随机游走
用\((i,j,k)\)表示孤立点有\(i\)个,点数>2的线段端点有\(j\)个,点数=2的线段端点有\(k\)个;即可以表示所有状态
起点为\((n,0,0)\),终点为\((0,0,0)\ (1,0,0)\ (0,0,2)\)
令\(f(i,j,k)\)表示从\((i,j,k)\)走到终点的期望,每个点包含自己转移到自己有\(\binom{3+1}{2}+1=7\)种转移,将自己转自己移项后即可由后继得到\(f\)值
整个转移过程可以使用Dfs实现
#include<bits/stdc++.h>
using namespace std;
const int Maxn=205;
int n;
bool vis[Maxn][Maxn][Maxn];
double f[Maxn][Maxn][Maxn];
inline bool Invalid(int i,int j,int k) {
if(i<0||j<0||k<0) return 1;
return 0;
}
double Dfs(int i,int j,int k) {
if(Invalid(i,j,k)) return 0;
if(vis[i][j][k])
return f[i][j][k];
double sum=0,sp=0;
double p;
if(i>=2) {
p=1.0*i/n*(i-1)/n;
sum+=p*Dfs(i-2,j,k+2);
sp+=p;
}
if(i&&j) {
p=1.0*i/n*j/n*2;
sum+=p*Dfs(i-1,j,k);
sp+=p;
}
if(i&&k) {
p=1.0*i/n*k/n*2;
sum+=p*Dfs(i-1,j+2,k-2);
sp+=p;
}
if(j>=2) {
p=1.0*j/n*(j-1)/n;
sum+=p*Dfs(i,j-2,k);
sp+=p;
}
if(j&&k) {
p=1.0*j/n*k/n*2;
sum+=p*Dfs(i,j,k-2);
sp+=p;
}
if(k>=4) {
p=1.0*k/n*(k-2)/n;
sum+=p*Dfs(i,j+2,k-4);
sp+=p;
}
vis[i][j][k]=1;
f[i][j][k]=(1.0/sp)*(sum+1);
return f[i][j][k];
}
int main() {
cin>>n;
f[1][0][0]=0; vis[1][0][0]=1;
f[0][0][2]=0; vis[0][0][2]=1;
f[0][0][0]=0; vis[0][0][0]=1;
Dfs(n,0,0);
cout<<fixed<<setprecision(9)<<f[n][0][0]<<endl;
return 0;
}
J(判奇环/偶环)
巨量分类讨论,主要难点在于判断原图是否存在偶环;而容易证明当图中存在复杂环时,该图一定存在偶环
利用dfs树check是否存在偶环:在dfs树上每遇到一条非树边,首先判断该简单环是否为偶环,否则把该边覆盖的祖孙链染色,若重复染色即存在偶环
分别对奇偶环的存在性分四类讨论,注意只存在奇环时需要check是否存在点数>4的链;不存在环时考虑两条短链拼起来的情况
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define fi first
#define se second
#define pb push_back
int n, m;
struct E
{
int to, nxt;
}e[400011];
int tag[200011];
int f1[100011], ne = 1;
int v1[100011], ve[200011], ff[100011], fe[100011], d[100011];
int in[100011],mx[100100],smx[100100];
void me(int x, int y)
{
e[++ne].to=y; e[ne].nxt=f1[x]; f1[x]=ne;
}
int has0, has1;
int tag1;
void upd(int x,int &a,int &b)
{
if(x>a) b=a,a=x;
else if(x>b) b=x;
}
void dfs1(int u, int pk,int dep)
{
v1[u] = 1,mx[u]=-n,smx[u]=-n;
if(dep>=3) tag1|=1;
//cout<<"dfs1: "<<u<<" "<<pk<<" "<<dep<<endl;
for (int k=f1[u];k;k=e[k].nxt)
if (!ve[k / 2] )
{
ve[k / 2] = 1;
if (!v1[e[k].to])
{
d[e[k].to] = d[u] + 1;
ff[e[k].to] = u;
fe[e[k].to] = k / 2;
dfs1(e[k].to, k,dep+1);
upd(mx[e[k].to]+1,mx[u],smx[u]);
if(mx[u]>=3||mx[u]+smx[u]+1>=4) tag1=1;
//cout<<u<<" "<<mx[u]<<" "<<smx[u]<<endl;
}
else
{
int len = d[u] - d[e[k].to] + 1;
//printf("--%d %d %d\n", u, e[k].to, len);
if (len %2 == 0)
{
has0 = 1;
}
else
{
has1 = 1;
if (len >= 5)
{
tag1 = 1;
}
int t = u;
while (t != e[k].to)
{
if (tag[fe[t]])
{
has0 = 1;
}
if (in[t] > 2)
{
tag1 = 1;
}
tag[fe[t]] = 1;
t = ff[t];
}
if (in[e[k].to] > 2)
{
tag1 = 1;
}
}
}
}
if(mx[u]<0) mx[u]=0;
}
vector<int> tmp;
struct TTT
{
int d[100011], vis[100011], maxp;
void dfs(int u)
{
if (d[u] > d[maxp]) maxp = u;
vis[u] = 1;
tmp.pb(u);
for (int k=f1[u];k;k=e[k].nxt)
if (!vis[e[k].to])
{
d[e[k].to] = d[u] + 1;
dfs(e[k].to);
}
}
}g1, g2;
int main()
{
int i, x, y;
scanf("%d%d", &n, &m);
for (i=1; i<=m; ++i)
{
scanf("%d%d", &x, &y);
me(x, y); me(y, x);
++in[x]; ++in[y];
}
for (i=1; i<=n; ++i)
if (!v1[i])
{
dfs1(i, 0,0);
}
if (n <= 3)
{
puts("-1");
}
else if (has0 && has1)
{
puts("0");
}
else if (has0)
{
puts("1");
}
else if (has1)
{
if (tag1) puts("1");
else puts("2");
}
else
{
int ans = 5;
for (i=1; i<=n; ++i)
if (!g1.vis[i])
{
tmp.clear();
g1.maxp = i;
g1.dfs(i);
g2.maxp = g1.maxp;
g2.dfs(g2.maxp);
if (g2.d[g2.maxp] >= 3)
{
ans = min(ans, 2);
}
else if (g2.d[g2.maxp] == 2)
{
int maxin = -1000;
for (auto xx : tmp)
{
maxin = max(maxin, in[xx]);
}
if (maxin >= 3)
{
ans = min(ans, 2);
}
else
{
ans = min(ans, 3);
}
}
else if (g2.d[g2.maxp] == 1)
{
if (m >= 2) ans = min(ans, 3);
else ans = min(ans, 4);
}
else//0
{
ans = min(ans, 5);
}
}
printf("%d\n", ans);
}
return 0;
}
L(哈希)
一个朴素的想法是每次枚举当前位置可以成功匹配的所有字符串\(s_j\),则有:
但显然每次遍历所有串复杂度过高,需要优化
而每个位置可以匹配到相同长度的串至多一个,同时串的长度至多有\(\sqrt{\sum|s_j|}\)种
因此只需预处理根号个长度的哈希表,每个表中存放所有该长度串的哈希值;每次对每个长度\(O(1)\)查找能否匹配,总复杂度为\(O(n\sqrt{\sum len})\)
需要使用双\(hash\),否则容易被卡
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef pair<int,int> pii;
typedef vector<int> vi;
const int MAXN=200100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
const int MOD=998244353;
#define fi first
#define se second
const int mod1=1000000009,mod2=1000000007,bas1=137,bas2=233;
char s[MAXN],t[MAXN];
int hsh1[MAXN],pw1[MAXN],hsh2[MAXN],pw2[MAXN],val[MAXN];
int n,m,tot,f[MAXN];
unordered_map<ll,int> lset,mp[500];
int lens[500];
ll getw(int x,int y)
{
return (((hsh1[y]-(1LL*hsh1[x-1]*pw1[y-x+1]%mod1)+mod1)%mod1)<<32LL)+
(hsh2[y]-(1LL*hsh2[x-1]*pw2[y-x+1]%mod2)+mod2)%mod2;
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
pw1[0]=pw2[0]=1;
rep(i,1,n)
{
pw1[i]=1LL*pw1[i-1]*bas1%mod1;
pw2[i]=1LL*pw2[i-1]*bas2%mod2;
hsh1[i]=(1LL*hsh1[i-1]*bas1+s[i]-'a'+1)%mod1;
hsh2[i]=(1LL*hsh2[i-1]*bas2+s[i]-'a'+1)%mod2;
}
m=read();
rep(i,1,m)
{
scanf("%s",t+1);
val[i]=read();
int len=strlen(t+1);
if(!lset.count(len)) lset[len]=++tot,lens[tot]=len;
int id=lset[len];
ll x=0,y=0;
rep(j,1,len)
x=(1LL*x*bas1+t[j]-'a'+1)%mod1,
y=(1LL*y*bas2+t[j]-'a'+1)%mod2;
mp[id][(x<<32LL)+y]=i;
}
f[0]=1;
rep(i,1,n)
{
f[i]=f[i-1];
rep(j,1,tot) if(i>=lens[j])
{
ll t=getw(i-lens[j]+1,i);
if(mp[j].count(t))
{
int id=mp[j][t];
(f[i]+=(1LL*f[i-lens[j]]*val[id])%MOD)%=MOD;
}
}
printf("%d%c",f[i],i==n?'\n':' ');
}
}
M(tarjan)
建图显然,每个排列依次向后连边,最终答案为每个点的后继个数
由于本题的建图方式,可以证明在scc缩点后得到一条链,因此直接tarjan缩点即可
#include<bits/stdc++.h>
using namespace std;
const int Maxn=500005,Maxm=1000005;
inline int read() {
char c; int rec=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9') rec=rec*10+c-'0',c=getchar();
return rec;
}
int n,m;
struct Branch {int next,to;} branch[Maxm];
int h[Maxn],cnt=0;
inline void add(int x,int y) {
branch[++cnt].to=y; branch[cnt].next=h[x]; h[x]=cnt; return ;
}
int Scc=0,sz[Maxn];
int dfn[Maxn],low[Maxn],ind=0;
int S[Maxn],vis[Maxn],top=0;
int bl[Maxn];
void Tarjan(int v) {
dfn[v]=low[v]=++ind;
S[++top]=v; vis[v]=1;
for(int i=h[v];i;i=branch[i].next) {
int j=branch[i].to;
if(!dfn[j]) Tarjan(j),low[v]=min(low[v],low[j]);
else if(vis[j]) low[v]=min(low[v],dfn[j]);
}
if(dfn[v]==low[v]) {
int p=0; ++Scc;
while(p!=v) {
p=S[top--],vis[p]=0,++sz[Scc];
bl[p]=Scc;
}
} return ;
}
vector<int> edge[Maxn];
map<pair<int,int>,int> occ;
int d[Maxn];
queue<int> Q;
int main() {
n=read(); m=read();
for(int i=1;i<=m;++i) {
int pre=0;
for(int j=1;j<=n;++j) {
int a=read();
if(pre) {
add(pre,a);
}
pre=a;
}
}
for(int i=1;i<=n;++i)
if(!dfn[i]) Tarjan(i);
for(int v=1;v<=n;++v) {
for(int i=h[v];i;i=branch[i].next) {
int j=branch[i].to;
if(bl[v]!=bl[j]&&occ[make_pair(bl[j],bl[v])]==0) {
edge[bl[j]].push_back(bl[v]);
occ[make_pair(bl[j],bl[v])]=1;
++d[bl[v]];
}
}
}
for(int i=1;i<=Scc;++i) {
if(d[i]==0) Q.push(i);
}
while(!Q.empty()) {
int v=Q.front(); Q.pop();
for(auto u:edge[v]) {
sz[u]+=sz[v];
--d[u];
if(d[u]==0)
Q.push(u);
}
}
for(int i=1;i<=n;++i) {
cout<<sz[bl[i]]-1<<" ";
}
}
N(签到)
使用string比较字符串
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
const int Maxn=100005;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
string a,b;
int main()
{
cin>>a>>b;
if(a>b) cout<<a<<">"<<b<<endl;
else if(a<b) cout<<a<<"<"<<b<<endl;
else cout<<a<<"="<<b<<endl;
}