noip模拟53
A. ZYB与售货机
签到题,是个基环树的套路.
对于有向图来说:
如果每个点的出度都是 \(1\),那么就是基环内向.
如果每个点的入度都是 \(1\),那么就是基环外向.
做这题的时候我没听说过基环树是啥,然后就自己糊了个 \(Tarjan\).
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define ull unsigned ll
#define re register ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=1e5+21;
ll n,ans,cnt,tot,tail;
ll s[N],c[N],d[N],a[N];
ll maxn[N],dfn[N],low[N],head[N],fa[N],vis[N],du[N],in[N],stk[N];
vector<ll> son[N];
// 每个点最多向外连 1 条边,但貌似入度不为 1.
// 不会有一个强连通连向另外一个强连通
struct Tarjan{
ll ts; ll head[N];
struct I { ll u,v,nxt; } e[N<<1];
inline void add(ll u,ll v){
e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u],
head[u]=ts;
}
} A;
void tarjan(ll u){
dfn[u]=++cnt,low[u]=cnt,stk[++tail]=u,vis[u]=1;
for(re i=A.head[u];i;i=A.e[i].nxt){
if(!dfn[A.e[i].v]){
tarjan(A.e[i].v),low[u]=min(low[u],low[A.e[i].v]);
}
else{
if(vis[A.e[i].v]) low[u]=min(low[u],dfn[A.e[i].v]);
}
}
if(dfn[u]==low[u]){
ll cur; tot++;
do{
cur=stk[tail--],vis[cur]=0,
fa[cur]=tot,son[tot].push_back(cur);
}while(cur!=u);
}
}
queue<ll> que;
inline void bfs(){
for(int i=1;i<=n;i++) if(!du[i]) que.push(i),vis[i]=1;
ll u,v;
while(que.size()){
u=que.front(),que.pop();
for(re i=A.head[u];i;i=A.e[i].nxt){
v=A.e[i].v,du[v]--,in[v]=max(in[v],d[v]-c[u]);
if(!du[v]){
que.push(v),vis[v]=1;
}
}
}
}
signed main(){
File(goods.in,goods.out);
n=read(); ll u,v,w,res,tmp;
for(re i=1;i<=n;i++){
s[i]=read(),c[i]=read(),d[i]=read(),a[i]=read();
}
for(re i=1;i<=n;i++){
maxn[s[i]]=max(maxn[s[i]],d[s[i]]-c[i]);
if(d[s[i]]>c[i]) A.add(i,s[i]),du[s[i]]++;
}
for(re i=1;i<=n;i++){
ans+=maxn[i]*(a[i]-1);
}
bfs();
for(re i=1;i<=n;i++){
if(vis[i]) ans+=in[i];
}
for(re i=1;i<=n;i++){
if((!vis[i]) and (!dfn[i])){
tarjan(i);
}
}
for(re i=1;i<=tot;i++){
res=0,tmp=1e10;
for(auto j : son[i]){
w=d[s[j]]-c[j],
res+=w,tmp=min(tmp,w);
}
for(auto j : son[i]){
w=d[s[j]]-c[j],
tmp=max(tmp,res-w+in[s[j]]);
}
ans+=tmp;
}
printf("%lld\n",ans);
exit(0);
}
/*
4
2 1 4 1
3 1 9 1
4 1 2 1
2 1 6 1
*/
B. ZYB玩字符串
又是一个字符题.
来自\(Puzzle\ Lover\)(蛇)的教训,这次果断不觉得像是\(Hash\)等字符的裸题了.
觉得像是个大模拟,于是考虑各种性质.
也想过会是动态规划,但是还是败了.
发现自己想的性质只能算是几个剪枝,就算是不剪也能过.
根据题解里的转移即可.
定义 \(f_{i,j}\) 为 \([i,j]\) 是否合法,是个 \(bool\) 类型的.
对于这种 \(dp\) 的转移,直接从合法状态转移就行了.
这里合法的定义就是,删掉独立的子串\(P\)后,剩下的能否组成前缀.
然后转移就行了.
几个剪枝,可以先找到 \(gcd\) 之后再转移,这个东西应该是很好想的.
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define ull unsigned ll
#define re register ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=211;
char s[N],c[N];
ll Ts,m,n;
ll f[N][N];
inline void Pre(){
Fill(f,0);
scanf("%s",s+1),n=strlen(s+1);
}
inline bool Match(ll st,ll en){
ll len=en-st+1; Fill(f,0);
for(re i=1;i<=n;i++) f[i][i]=(s[i]==s[st]);
for(re i=1;i<=len;i++) c[i]=s[i+st-1];
for(re l=2;l<=n;l++){
for(re i=1,j=i+l-1;i+l-1<=n;i++,j=i+l-1){
f[i][j]|=f[i][j-1]&(s[j]==c[(j-i)%len+1]);
for(re k=len;k<=j-i;k+=len){
f[i][j]|=f[i][j-k]&f[j-k+1][j];
}
}
}
return f[1][n];
}
inline void Work(){
for(re l=1;l<=n;l++){
if(n%l) continue;
for(re i=1;i<=n-l+1;i++){
if(Match(i,i+l-1)){
for(re j=i;j<=i+l-1;j++) putchar(s[j]);
puts(""); return ;
}
}
}
}
signed main(){
File(string.in,string.out);
Ts=read();
while(Ts--){
Pre(),Work();
}
exit(0);
}
C. 午餐
最短路转移状态,比较帅.
膜拜 \(zero4338\) 大神 和 \(MAX\)_\(QAQ\) 大神.
发现限制只有两个.
一个是 \(-1\) 的限制,另外一个是 \([L,R]\) 的区间限制.
于是我们也可以选择搞两个数组来记录限制,
另外,两个限制也是一个影响另一个的.
可以理解为限制也是有优先级的,\(-1\) 的限制显然是要更高级.
细节处理一下就好了..
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define ull unsigned ll
#define re register ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=2e5+21,inf=0x3f3f3f3f;
ll m,n,ts;
ll g[N],f1[N],f2[N],vis[N],head[N],ans[N];
queue<ll> que;
struct I { ll u,v,l,r,nxt; } e[N<<1];
inline void add(ll u,ll v,ll l,ll r){
e[++ts].u=u,e[ts].v=v,e[ts].l=l,e[ts].r=r,
e[ts].nxt=head[u],head[u]=ts;
}
inline void bfs1(){
ll u,v;
while(que.size()){
u=que.front(),que.pop();
for(re i=head[u];i;i=e[i].nxt){
if(f1[u]>e[i].r and e[i].l+1>f1[e[i].v]){
f1[e[i].v]=e[i].l+1;
if(!vis[e[i].v]) que.push(e[i].v);
vis[e[i].v]=1;
}
}
vis[u]=0;
}
}
inline void bfs2(){
ll u,v; Fill(f2,0x3f);
que.push(1),f2[1]=0,vis[1]=1;
while(que.size()){
u=que.front(),que.pop();
for(re i=head[u];i;i=e[i].nxt){
v=max(max(f2[u],e[i].l),f1[e[i].v]);
if(v<f2[e[i].v] and v<=e[i].r){
if(!vis[e[i].v]) que.push(e[i].v);
f2[e[i].v]=v,vis[e[i].v]=1;
}
}
vis[u]=0;
}
}
signed main(){
File(lunch.in,lunch.out);
n=read(),m=read(); ll u,v,l,r;
for(re i=1;i<=m;i++){
u=read(),v=read(),l=read(),r=read(),
add(u,v,l,r),add(v,u,l,r);
}
for(int i=1;i<=n;i++){
g[i]=read();
if(g[i]<0) que.push(i),f1[i]=inf,vis[i]=1;
}
bfs1();
if(f1[1]>=1) puts("Impossible"),exit(0);
bfs2();
for(re i=1;i<=n;i++){
if(f2[i]>=1e9 and g[i]==1) puts("Impossible"),exit(0);
}
for(re i=1;i<=ts;i+=2){
if(f2[e[i].u]>e[i].r or f2[e[i].v]>e[i].r){
printf("%lld\n",e[i].l);
}
else{
printf("%lld\n",max(e[i].l,max(f2[e[i].u],f2[e[i].v])));
}
}
exit(0);
}
D. 计数
对于 \(M==0\),形式上是卡特兰数,其实也就是卡特兰数.
这里提醒自己,数学知识不仅仅是流于表面形式的,
但有一部分只根据表面形式就可以看出来是数学想法.
考虑一下题目中的限制,发现前序遍历和中序遍历两个东西的差距就在于左子树和根节点的遍历顺序.
于是可以考虑转化,把题目中的限制转化为左子树和根的位置关系,也可以看成是根的左子树大小限制.
于是按着思路码一码就好了.
D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define ull unsigned ll
#define re register ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=615,mod=1e9+7;
ll Ts,n,m;
ll l[N],r[N];
ll dp[N][N];
ll dfs(ll u,ll siz){
if((!siz) or u==n) return dp[u][siz]=1;
if(~dp[u][siz]) return dp[u][siz];
ll le=l[u],ri=min(r[u],siz-1);
dp[u][siz]=0;
for(re i=le;i<=ri;i++){
dp[u][siz]=(dp[u][siz]+dfs(u+1,i)*dfs(u+i+1,siz-i-1)%mod)%mod;
}
return dp[u][siz];
}
inline void Work(){
Fill(dp,-1);
n=read(),m=read(); ll x,y;
for(re i=1;i<=n;i++) l[i]=0,r[i]=n-i+1;
for(re i=1;i<=m;i++){
x=read(),y=read();
if(x>y) l[y]=max(l[y],x-y);
else r[x]=min(r[x],y-x-1);
}
printf("%lld\n",dfs(1,n));
}
signed main(){
File(count.in,count.out);
Ts=read();
while(Ts--) Work();
exit(0);
}