191105-模拟测试12
T1 panwang
题目描述
解析
首先,很显然,该题只有两种情况:
1,打完御符(前提是能打完),记为
a
n
s
1
ans1
ans1
2,根本不打御符,记为 a n s 2 ans2 ans2
最后的答案即是 m a x ( a n s 1 , a n s 2 ) max(ans1,ans2) max(ans1,ans2)
对于第一种情况,我们需要用能力值刚好比御符大或等于的兵符来打掉御符(很显然,这样是最优的)
然后就是坑点所在,考场上我直接判断御符打完后,就直接给 a n s 1 ans1 ans1加上了剩余所有兵符的能力值,显然这样是错的,因为我们忽略了对手手中能力值为负的兵符,若我们用剩余的兵符去攻击对手手中负的兵符,显然我们会造成更大的伤害,同时,我们最后剩余的兵符中,不能给 a n s 1 ans1 ans1累加上我们手中能力值为负的兵符。
对于第二种情况就比较简单了,直接用我们手中最大的兵符,去打掉对手手中的最小的兵符,一定为最优的(至于正确性,感性理解,我就懒得证明了)
最后在算法上还有一点需要注意的,因为我们注意到能力值绝对值的最大值为100,因此我们开个大小为200的桶就可以轻松解决该题(而考场上没有考虑到,因此用了3个sort,会tle掉)
题解
#include<bits/stdc++.h>
#define M 207
using namespace std;
long long n,m,q,ans2,ans1,a[M],a1[M],b[M],c[M],c1[M];
long long read()
{
int f=1;
long long re=0;
char ch;
for(ch=getchar();(ch>'9'||ch<'0')&&ch!='-';ch=getchar());
if(ch=='-'){
f=-1;
ch=getchar();
}
for(;ch>='0'&&ch<='9';ch=getchar())
re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
signed main()
{
//freopen("panwang3.in","r",stdin);
//freopen("panwang.out","w",stdout);
long long x,y;
n=read();
m=read();
q=read();
for(int i=1;i<=n;i++){
x=read();
y=read();
a[x+100]+=y;
a1[x+100]+=y;
}
for(int i=1;i<=m;i++){
x=read();
y=read();
b[x+100]+=y;
}
for(int i=1;i<=q;i++){
x=read();
y=read();
c[x+100]+=y;
c1[x+100]+=y;
}
for(int i=0,j=0;i<=200&&j<=200;)
{
while(!a[i]) i++;
while(!b[j]) j++;
if(i>200||j>200) break;
while(i<j) i++;
long long v=min(a[i],b[j]);
a[i]-=v;
b[j]-=v;
}
bool bj=0;
for(int i=0;i<=200;i++)
if(b[i]){
bj=1;
break;
}
if(!bj)
{
for(int i=200,j=0;i>=0&&j<=100;)
{
while(!a[i]) i--;
while(!c[j]) j++;
if(i<0||j>100) break;
if(i<=j) break;
long long v=min(a[i],c[j]);
a[i]-=v;
c[j]-=v;
ans1+=(v*(i-j));
}
for(int i=101;i<=200;i++)
ans1+=(a[i]*(i-100));
}
for(int i=200,j=0;i>=0&&j<=200;)
{
while(!a1[i]) i--;
while(!c1[j]) j++;
if(i<0||j>200) break;
if(i<=j) break;
long long v=min(a1[i],c1[j]);
a1[i]-=v;
c1[j]-=v;
ans2+=(v*(i-j));
}
printf("%lld",max(ans1,ans2));
return 0;
}
T2 zhuhzu
题目描述
解析
考场上想出了次小生成树,但是码量较大,不想打了
话归正传,首先根据题意,我们知道最小的合法道路集合是所有合法的生成树中最小的一棵(不一定是原图的最小生成树,也可能是原图的次小生成树,由所给的 x x x值来定)
于是先求出原图的最小生成树,再分类讨论( s u m sum sum为原图最小生成树的大小, x x x为题目中所给定的值)
1, s u m > x sum>x sum>x 无解
2, s u m = x sum=x sum=x 于是次小生成树就会有两种,一种是大小也等于 x x x,另一种是大小大于 x x x的,大于 x x x的生成树随便染色即可,因此我们只需统计大小等于 x x x的数量 c n t 1 cnt1 cnt1
因此答案为:
2
m
−
2
∗
2
m
−
(
n
−
1
)
−
c
n
t
1
2^m-2*2^{m-(n-1)-cnt1}
2m−2∗2m−(n−1)−cnt1
(解释:因为
s
u
m
=
x
sum=x
sum=x,因此不存在小于
x
x
x的生成树,所以理论上所有边都可以随便染两种染色即
2
m
2^m
2m,但实际上要减去所有大小等于x的生成树上的边同色的情况,即
2
∗
2
m
−
(
n
−
1
)
−
c
n
t
1
2*2^{m-(n-1)-cnt1}
2∗2m−(n−1)−cnt1(为什么要
∗
2
*2
∗2,因为有两种颜色))
3, s u m < x sum<x sum<x 于是次小生成树会有三种,一种是大于 x x x的,同上,直接不管就好了,等于x的,可以替换的边数记为 c n t 2 cnt2 cnt2,小于x的,可以替换的边数记为 c n t 3 cnt3 cnt3
因此答案为:
2
∗
(
2
m
−
(
n
−
1
)
−
c
n
t
3
−
2
m
−
(
n
−
1
)
−
c
n
t
2
−
c
n
t
3
)
2*(2^{m-(n-1)-cnt3}-2^{m-(n-1)-cnt2-cnt3})
2∗(2m−(n−1)−cnt3−2m−(n−1)−cnt2−cnt3)
(解释:首先理论上除去大小小于
x
x
x的生成树上的边,其他边都可以随便染色,即
2
m
−
(
n
−
1
)
−
c
n
t
3
2^{m-(n-1)-cnt3}
2m−(n−1)−cnt3,但实际上要减去大小等于
x
x
x的生成树上的边同色的请况,即
2
m
−
(
n
−
1
)
−
c
n
t
2
−
c
n
t
3
2^{m-(n-1)-cnt2-cnt3}
2m−(n−1)−cnt2−cnt3,(为什么要
∗
2
*2
∗2,因为有两种颜色))
同时需要解释下次小生成树,这里说的次小生成树,实际上是和每条边所对应的,即 使该边强制出现在原图的最小生成树中的次小生成树(有点绕口,我也说不太清楚),而且该次小生成树与原图最小生成树有且只有一条边的差异(可以去做一道例题 严格次小生成树)
其次,我们从原图最小生成树中替换下的边一定是,要替换上的边的两个端点,在原图最小生成树中所对应的链上的最大的边。
题解
#include<bits/stdc++.h>
#define M 200005
#define int long long
using namespace std;
const int mod=1e9+7;
int ans[M],to[M*2],first[M],nxt[M*2],n,m,k,cnt,num,tot,w[M*2],t,cur=1,fa[M],dep[M],f[M][21],sum,maxn[M][21],ss,s1,s2;
bool bj[M],vis[M];
int read(){
int f=1,re=0;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
struct qrx{
int x,y,z,v;
}a[M];
bool comp(qrx a,qrx b){
return a.z<b.z;
}
int ksm(int a,int b){//快速幂
int ans=1;
while(b){
if(b&1) ans=(ans*a)%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void add2(int x,int y,int z){
nxt[++num]=first[x];
first[x]=num;
to[num]=y;
w[num]=z;
}
void add1(int x,int y,int z){
a[++tot].x=x;
a[tot].y=y;
a[tot].z=z;
}
void kruskal(){//求最小生成树
int cnt=0;
sort(a+1,a+tot+1,comp);
for(int i=1;i<=tot;i++){
int u=find(a[i].x);
int v=find(a[i].y);
if(u!=v){
fa[u]=v;
sum+=a[i].z;
cnt++;
vis[i]=1;
add2(a[i].x,a[i].y,a[i].z);
add2(a[i].y,a[i].x,a[i].z);
}
if(cnt==n-1) break;
}
}
void init(int u,int faa){
dep[u]=dep[faa]+1;
for(int i=first[u];i;i=nxt[i]){
int v=to[i];
if(v==faa) continue;
f[v][0]=u;
maxn[v][0]=w[i];
init(v,u);
}
}
void pre(){
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++){
f[j][i]=f[ f[j][i-1] ][i-1];
maxn[j][i]=max(maxn[j][i-1],maxn[f[j][i-1]][i-1]);
}
}
int Lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int findmax(int x,int lca)
{
int ans=0;
for(int i=20;i>=0;i--)
if(dep[ f[x][i] ] >= dep[lca]){
ans=max(ans,maxn[x][i]);
x=f[x][i];
}
return ans;
}
void work(){//求次小生成树
pre();
for(int i=1;i<=tot;i++){
if(vis[i]) continue;
int x=a[i].x,y=a[i].y,z=a[i].z;
int lca=Lca(x,y);
a[i].v=z-max(findmax(x,lca),findmax(y,lca));
}
}
void clear(){//多组数据要清空
ss=s1=s2=tot=sum=num=0;
memset(first,0,sizeof(first));
memset(nxt,0,sizeof(nxt));
memset(w,0,sizeof(nxt));
memset(to,0,sizeof(nxt));
memset(f,0,sizeof(f));
memset(vis,0,sizeof(vis));
memset(dep,0,sizeof(dep));
memset(a,0,sizeof(a));
memset(fa,0,sizeof(fa));
memset(maxn,0,sizeof(maxn));
}
signed main(){
//freopen("zhuzhu3.in","r",stdin);
//freopen("zhuzhu.out","w",stdout);
scanf("%lld",&t);
while(t--){
clear();
n=read();m=read();k=read();
int x,y,z;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
x=read();
y=read();
z=read();
add1(x,y,z);
}
kruskal();
if(sum>k) {//当最小生成树大于k时,无解
printf("0\n");
continue;
}
init(1,0);
work();
if(sum==k){//sum==k的请况,统计次小生成树的大小也等于k的边数
for(int i=1;i<=tot;i++){
if(vis[i]) continue;
if(!a[i].v) ss++;
}
printf("%lld\n",(ksm(2,m)-(2*ksm(2,m-(n-1)-ss)%mod)+mod)%mod);
continue;
}
if(sum<k){//sum<k的请况,统计次小生成树的大小小于k的边数,次小生成树的大小等于k的边数
for(int i=1;i<=tot;i++){
if(vis[i]) continue;
if(a[i].v+sum<k) s1++;
if(a[i].v+sum==k) s2++;
}
printf("%lld\n",2*((ksm(2,m-(n-1)-s1)-ksm(2,m-(n-1)-s1-s2)+mod)%mod)%mod);
}
}
return 0;
}
T3 shuawang
题目描述
解析
我不会 (考场上写了个暴力,20分)
直接放上题解
题解
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read(){
ll x=0,w=1;
char ch=0;
while (ch<'0' || ch>'9'){
ch=getchar();
if (ch=='-') w=-1;
}
while (ch<='9' && ch>='0'){
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*w;
}
namespace pb_ds{
namespace io{
const int MaxBuff=1<<15;
const int Output=1<<23;
char B[MaxBuff],*S=B,*T=B;
#define getc() ((S==T)&&(T=(S=B)+fread(B,1,MaxBuff,stdin),S==T)?0:*S++)
char Out[Output],*iter=Out;
inline void flush(){
fwrite(Out,1,iter-Out,stdout);
iter=Out;
}
}
template<class Type> inline void Print(register Type x,register char ch='\n'){
using namespace io;
if(!x) *iter++='0';
else{
if(x<0) *iter++='-',x=-x;
static int s[100];
register int t=0;
while(x) s[++t]=x%10,x/=10;
while(t) *iter++='0'+s[t--];
}
*iter++=ch;
}
}
using namespace pb_ds;
const int N=5e4+5;
const int M=21;
const int L=17;
const int Mod=1e9+7;
const ll inf=(ll)1e18+1;
int T,n,m,q;
bool p[N][10];
int fail[M],val[L][N][M],trans[M][10],pw[L]={10};
ll dp[N][M],rkl[L][N][M],rkr[L][N][M];
char str[M],s[N],ed[L][N][M];
inline ll fix(ll x){
return x<inf?x:inf;
}
inline void KMP(){
fail[0]=fail[1]=0;
for (int i=2;i<=m;++i){
int now=fail[i-1];
while (now && str[now+1]!=str[i]) now=fail[now];
if (str[now+1]==str[i]) fail[i]=now+1;
else fail[i]=0;
}
}
inline void init(){
for (int i=0;i<m;++i){
for (int j=0;j<=9;++j){
int now=i;
while (now && str[now+1]!=j+'0') now=fail[now];
if (str[now+1]!=j+'0') trans[i][j]=0;
else trans[i][j]=now+1;
}
}
for (int j=0;j<=9;++j) trans[m][j]=m;
}
inline void input(){
n=read(),q=read();
scanf("%s%s",str+1,s+1);
m=strlen(str+1);
for (int i=1;i<=n;++i){
if (s[i]=='?') memset(p[i],1,sizeof(p[i]));
else memset(p[i],0,sizeof(p[i])),p[i][s[i]-'0']=1;
}
KMP(),init();
}
inline int query(ll k){
if (k>dp[1][0]) return -1;
int x=1,y=0,res=0;
while (x<=n){
for (int i=L-1;~i;--i){
if (x+(1<<i)<=n+1 && rkl[i][x][y]<k && k<=rkr[i][x][y]){
res=(1ll*res*pw[i]+val[i][x][y])%Mod;
k-=rkl[i][x][y],y=ed[i][x][y],x+=(1<<i);
}
}
if (x>n) break;
for (int i=0;i<=9;++i){
if (k>dp[x+1][trans[y][i]]) k-=dp[x+1][trans[y][i]];
else{
res=(10ll*res+i)%Mod;
++x,y=trans[y][i];
break;
}
}
}
return res;
}
int main(){
freopen("shuawang.in","r",stdin);
freopen("shuawang.out","w",stdout);
for (int i=1;i<L;++i)
pw[i]=1ll*pw[i-1]*pw[i-1]%Mod;
T=read();
while (T--){
input();
for (int i=0;i<=m;++i)
dp[n+1][i]=(i==m);
for (int i=n;i;--i){
for (int j=0;j<=m;++j){
dp[i][j]=0;
ll mx=-1;
int nxt=-1;
for (int k=0;k<=9;++k){
if (p[i][k]){
dp[i][j]=fix(dp[i][j]+dp[i+1][trans[j][k]]);
if (dp[i+1][trans[j][k]]>mx) nxt=k,mx=dp[i+1][trans[j][k]];
}
}
ed[0][i][j]=trans[j][nxt];
val[0][i][j]=nxt,rkl[0][i][j]=0;
for (int k=0;k<nxt;++k)
if (p[i][k]) rkl[0][i][j]=fix(rkl[0][i][j]+dp[i+1][trans[j][k]]);
rkr[0][i][j]=fix(rkl[0][i][j]+dp[i+1][trans[j][nxt]]);
}
}
for (int k=1;k<L;++k){
int len=(1<<(k-1));
for (int i=1;i+(1<<k)<=n+1;++i){
for (int j=0;j<=m;++j){
int x=ed[k-1][i][j];
ed[k][i][j]=ed[k-1][i+len][x];
val[k][i][j]=(1ll*val[k-1][i][j]*pw[k-1]+val[k-1][i+len][x])%Mod;
rkl[k][i][j]=fix(rkl[k-1][i][j]+rkl[k-1][i+len][x]);
rkr[k][i][j]=fix(rkl[k-1][i][j]+rkr[k-1][i+len][x]);
}
}
}
while (q--){
ll k=read();
Print(query(k));
}
io::flush();
}
return 0;
}
暴力代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q,m,x,cnt,ans[20000009],p[1009],t;
char s1[1009],s2[1009];
const int mod=1e9+7;
void check(){
bool bj=0;
for(int i=1,j=0;i<=n;i++){
while(j>0&&s1[j+1]!=s2[i]) j=p[j];
if(s1[j+1]==s2[i]) j++;
if(j==m){
bj=1;
break;
}
}
if(bj){
cnt++;
for(int i=1;i<=n;i++)
ans[cnt]=(ans[cnt]<<3)+(ans[cnt]<<1)+s2[i]-'0';
}
}
void dfs(int pos){
if(pos==n+1){
check();
return;
}
if(isdigit(s2[pos]))
dfs(pos+1);
else
for(int i=0;i<=9;i++){
s2[pos]=i+'0';
dfs(pos+1);
s2[pos]='?';
}
}
signed main(){
freopen("shuawang.in","r",stdin);
freopen("shuawang.out","w",stdout);
scanf("%lld",&t);
while(t--){
cnt=0;
memset(ans,0,sizeof(ans));
memset(p,0,sizeof(p));
scanf("%lld%lld",&n,&q);
scanf("%s",s1+1);
m=strlen(s1+1);
scanf("%s",s2+1);
for(int i=2,j=0;i<=m;i++){
while(j>0&&s1[j+1]!=s1[i]) j=p[j];
if(s1[j+1]==s1[i]) j++;
p[i]=j;
}
dfs(1);
sort(ans+1,ans+cnt+1);
for(int i=1;i<=q;i++){
scanf("%lld",&x);
if(x<=cnt)
printf("%lld\n",ans[x]%mod);
else
printf("-1\n");
}
}
return 0;
}