Contest:https://ac.nowcoder.com/acm/contest/1083#question
当时comet和这个重了,就去打comet了(谁让comet奖品看起来更好一点呢),结果被吊打,最后一个小时才来写这场,想着混个签到抽抽短袖,结果发现题目意外的简单点?(结果成了二分场?是不是数据很水啊....)
A-abc(二分)
题目链接:https://ac.nowcoder.com/acm/contest/1083/A
题目大意:给出一个只含小写字母的字符串S,找出S的子序列abc的个数。
思路:遍历a,二分每个a后面的第一个b,再找出c,随便过。。。
ACCode:
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
char S[MAXN];
int A[MAXN],B[MAXN],C[MAXN];
int tota,totb,totc;
int main(){
scanf("%s",S+1);
int len=strlen(S+1);
tota=totb=totc=0;
for(int i=1;i<=len;++i){
if(S[i]=='a') A[++tota]=i;
else if(S[i]=='b') B[++totb]=i;
else if(S[i]=='c') C[++totc]=i;
}
ll ans=0;
for(int i=1;i<=tota;++i){
int l=1,r=totb,mid;
while(l<=r){//找到第一个B[mid]>=A[i]
mid=(l+r)>>1;
if(B[mid]<=A[i]) l=mid+1;
else r=mid-1;
}
int posb=l;
if(posb>totb) continue;
for(int j=posb;j<=totb;++j){
l=1,r=totc;
while(l<=r){
mid=(l+r)>>1;
if(C[mid]<=B[j]) l=mid+1;
else r=mid-1;
}
int posc=l;
if(posc>totc) break;
ans+=totc-posc+1;
}
}printf("%lld\n",ans);
}
B-字串查询(贪心+二分)
题目链接:https://ac.nowcoder.com/acm/contest/1083/B
题目大意:给出一个字符串S,对于q次询问,每次给出字符串p,判断p是不是S的子序列
思路:对于p[i],肯定是找第一个出现在S中的字符,所以二分就好了,随便过。。
ACCode:
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
char S[MAXN],P[MAXN];
int Pos[30][MAXN],Totx[30],Idx[30];
int n,q;
int main(){
scanf("%d%d",&n,&q);
scanf("%s",S+1);
for(int i=1;i<=n;++i){
int ch=S[i]-'a'+1;
Pos[ch][++Totx[ch]]=i;
}
while(q--){
scanf("%s",P+1);int len=strlen(P+1);
for(int i=0;i<30;++i) Idx[i]=0;
int flag=1,l,r,mid,pre=Pos[P[1]-'a'+1][1];
for(int i=2;i<=len;++i){
int ch=P[i]-'a'+1;
l=1,r=Totx[ch];
while(l<=r){
mid=(l+r)>>1;
if(Pos[ch][mid]<=pre) l=mid+1;
else r=mid-1;
}
if(l>Totx[ch]){
flag=0;break;
}pre=Pos[ch][l];
}
if(flag) printf("YES\n");
else printf("NO\n");
}
}
C-勾股定理(数论结论)
题目链接:https://ac.nowcoder.com/acm/contest/1083/C
题目大意:给出一个长度l,判断该长度是否是直角三角形的一边,边都只能是整数。输出另外两边。
思路:之前碰到过这道题,一个结论题,问了问队友,结果数论队友直接秒掉了。。
ACCode:
int main(){
ll a,b;
cin>>a>>b;
int flag=0;
if(a%2&&a>1){
ll n=(a-1)/2;
cout<<2*n*n+2*n<<" "<<2*n*n+2*n+1<<endl;
return 0;
}
if(a%2==0&&a>=4){
ll n=a/2;
cout<<n*n-1<<" "<<n*n+1<<endl;
return 0;
}
cout<<-1<<endl;
return 0;
}
D-羊吃草(暴力+二分匹配)
题目链接:https://ac.nowcoder.com/acm/contest/1083/D
题目大意:中文题
思路:想着先写一个q*400的匈牙利,T了再说。。结果就过了。。
队友完全暴力,q*(r-l+1)*n的查询。。都过了。。
ACCode:
const int MAXN=1e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
struct Edge1{
int v,val,nxt;
Edge1(int _v=0,int _val=0,int _nxt=0){
v=_v;val=_val;nxt=_nxt;
}
};
Edge1 Edge[MAXN*MAXN];
int Head[MAXN],Ecnt;
int L[MAXN],R[MAXN];
int Flag[MAXN],Vis[MAXN];
int ql,qr;
int n,q;
void Intt(){
clean(Head,-1);Ecnt=0;
}
void AddEdge(int u,int v,int val){
Edge[Ecnt]=Edge1(v,val,Head[u]);
Head[u]=Ecnt++;
}
int DFS(int u){
for(int i=Head[u];i+1;i=Edge[i].nxt){
int v=Edge[i].v;//对应编号的羊
if(Vis[v]==0){
Vis[v]=1;
if(Flag[v]==0||DFS(Flag[v])){
Flag[v]=u;
return 1;
}
}
}return 0;
}
int main(){
scanf("%d%d",&n,&q);
Intt();
for(int i=1;i<=n;++i) scanf("%d",&L[i]);
for(int i=1;i<=n;++i) scanf("%d",&R[i]);
for(int i=1;i<=n;++i){
for(int j=L[i];j<=R[i];++j){
AddEdge(i,400+j,1);AddEdge(400+j,i,0);
}
}
while(q--){
scanf("%d%d",&ql,&qr);
ql+=400;qr+=400;
clean(Vis,0);clean(Flag,0);
int ans=0;
for(int i=ql;i<=qr;++i){
clean(Vis,0);
if(DFS(i)) ans++;
}printf("%d\n",ans);
}
}
E-数列(思维+二分)
题目链接:https://ac.nowcoder.com/acm/contest/1083/E
题目大意:中文题
思路:一开始没什么思路,想着贪心,但一直模拟不出贪心策略,后来听见队友来了句二分。。woc!可以二分连续的区间数量,利用贪心思想,尽量平摊区间长度,多余的加前后都行。结果就二分出来了。。。
ans=n-(不连续区间数量),找出最小的不连续递增区间数量就好了。
ACCode:
const int MAXN=1e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
int n,m;
int Judge(int mid){
int len=n/mid,del=n%mid;//每段len个元素,剩下del个元素分到前面
int Sum=del*(1+len+1)*(len+1)/2+(mid-del)*(1+len)*len/2;
return Sum;//花费
}
int main(){
scanf("%d%d",&n,&m);
int l=1,r=n,mid;
while(l<=r){//找到第一个符合要求的mid
mid=(l+r)>>1;
if(Judge(mid)<=m) r=mid-1;//符合要求
else l=mid+1;
}
int len=n/l,del=n%l;
for(int i=1;i<=l;++i){
if(del){
for(int j=1;j<=len+1;++j) printf("%d ",j);
--del;
}
else{
for(int j=1;j<=len;++j) printf("%d ",j);
}
}
}
F-ABCBA(矩阵DP+树剖)
题目链接:https://ac.nowcoder.com/acm/contest/1083/F
题目大意:中文题
思路:有点类似上星期的南昌C?据说也是抄的CF Goodbye2017E。。
用矩阵维护子序列的数量,加个LCA,用树剖维护子序列。注意一下线段树合并。
但是还是没思路(因为这两道都没有写出来QAQ)
赛后看的题解。
https://ac.nowcoder.com/discuss/248083?type=101&order=0&pos=19&page=0
需要注意的是,查询的字符串是v->u的顺序,所以两个链所合并的顺序是不一样的。
但听队友说有一个主席树的解法,常数更小。抽空再看看把QAQ
ACCode:
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=3e4+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e4+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
struct SegTree{
struct Node{
int l,r;
int dp[6][6];
void Intt(){
for(int i=1;i<=5;++i){
for(int j=1;j<=5;++j){
dp[i][j]=0;
}
}
}
friend Node operator + (Node a,Node b){
Node ans;
ans.l=min(a.l,b.l);ans.r=max(a.r,b.r);ans.Intt();
for(int i=1;i<=5;++i){
for(int j=1;j<=5;++j){
ans.dp[i][j]=(a.dp[i][j]+b.dp[i][j])%MOD;
}
}
for(int i=1;i<=5;++i){
for(int j=1;j<=5;++j){
for(int k=j+1;k<=5;++k){
ans.dp[i][k]=(ans.dp[i][k]+a.dp[i][j]*b.dp[j+1][k])%MOD;
}
}
}return ans;
}
};
Node Tree[MAXN<<2][2];
void PushUp(int rt){
Tree[rt][0]=Tree[rt<<1][0]+Tree[rt<<1|1][0];
Tree[rt][1]=Tree[rt<<1|1][1]+Tree[rt<<1][1];
}
void Build(int l,int r,int rt,char A[]){
Tree[rt][0].l=l;Tree[rt][0].r=r;
if(l==r){
if(A[l]=='A') Tree[rt][0].dp[1][1]=Tree[rt][0].dp[5][5]=1;
if(A[l]=='B') Tree[rt][0].dp[2][2]=Tree[rt][0].dp[4][4]=1;
if(A[l]=='C') Tree[rt][0].dp[3][3]=1;
Tree[rt][1]=Tree[rt][0];
return ;
}int mid=(l+r)>>1;
Build(l,mid,rt<<1,A);Build(mid+1,r,rt<<1|1,A);
PushUp(rt);
}
Node Query(int ql,int qr,int rt,int opt){
if(ql<=Tree[rt][opt].l&&Tree[rt][opt].r<=qr){
return Tree[rt][opt];
}
if(qr<=Tree[rt<<1][opt].r) return Query(ql,qr,rt<<1,opt);
else if(ql>=Tree[rt<<1|1][opt].l) return Query(ql,qr,rt<<1|1,opt);
else{
if(opt) return Query(ql,qr,rt<<1|1,opt)+Query(ql,qr,rt<<1,opt);
else return Query(ql,qr,rt<<1,opt)+Query(ql,qr,rt<<1|1,opt);
}
}
void Show(int rt){
printf("l=%d r=%d dp:\n",Tree[rt][0].l,Tree[rt][0].r);
for(int i=1;i<=5;++i){
for(int j=1;j<=5;++j){
printf("%d ",Tree[rt][0].dp[i][j]);
}
}
if(Tree[rt][0].l==Tree[rt][0].r) return ;
Show(rt<<1);Show(rt<<1|1);
}
};
struct Edge1{
int v,nxt;
Edge1(int _v=0,int _nxt=0){
v=_v;nxt=_nxt;
}
};
SegTree Seg;
Edge1 Edge[MAXN<<2];
int Head[MAXN],Ecnt;
int Deep[MAXN],Fa[MAXN],Size[MAXN],Son[MAXN];
int Idx[MAXN],Icnt;
int Top[MAXN];
char S[MAXN],B[MAXN];
int n,q;
void Intt(){
clean(Head,-1);Ecnt=0;
clean(Deep,0);clean(Fa,-1);clean(Size,0);clean(Son,-1);
Icnt=0;
}
void AddEdge(int u,int v){
Edge[Ecnt]=Edge1(v,Head[u]);
Head[u]=Ecnt++;
}
int DFS1(int u,int fa,int dep){
Deep[u]=dep;Fa[u]=fa;Size[u]=1;
int maxson=-1;
for(int i=Head[u];i+1;i=Edge[i].nxt){
int v=Edge[i].v;
if(v==fa) continue;
Size[u]+=DFS1(v,u,dep+1);
if(Size[v]>maxson){
maxson=Size[v];Son[u]=v;
}
}return Size[u];
}
void DFS2(int u,int topfa){
Idx[u]=++Icnt;Top[u]=topfa;B[Idx[u]]=S[u];
if(Son[u]==-1) return ;
DFS2(Son[u],topfa);
for(int i=Head[u];i+1;i=Edge[i].nxt){
int v=Edge[i].v;
if(Idx[v]==0) DFS2(v,v);
}
}
int QueryCnt(int a,int b){//b->a
int aa=(MAXN<<2)-1,bb=(MAXN<<2)-2;
Seg.Tree[aa][0].Intt();Seg.Tree[bb][0].Intt();
while(Top[a]!=Top[b]){
if(Deep[Top[a]]<Deep[Top[b]]){//b向上提
Seg.Tree[bb][0]=Seg.Tree[bb][0]+Seg.Query(Idx[Top[b]],Idx[b],1,1);
b=Fa[Top[b]];
}
else{//a向上提
Seg.Tree[aa][0]=Seg.Query(Idx[Top[a]],Idx[a],1,0)+Seg.Tree[aa][0];
a=Fa[Top[a]];
}
}
if(Deep[a]<Deep[b]){
Seg.Tree[bb][0]=(Seg.Tree[bb][0]+Seg.Query(Idx[a],Idx[b],1,1))+Seg.Tree[aa][0];
return Seg.Tree[bb][0].dp[1][5]%MOD;
}
else{
Seg.Tree[aa][0]=(Seg.Tree[bb][0]+Seg.Query(Idx[b],Idx[a],1,0))+Seg.Tree[aa][0];
return Seg.Tree[aa][0].dp[1][5]%MOD;
}
}
int main(){
Intt();
scanf("%d%d",&n,&q);
scanf("%s",S+1);
for(int i=1;i<n;++i){
int u,v;scanf("%d%d",&u,&v);
AddEdge(u,v);AddEdge(v,u);
}DFS1(1,-1,1);DFS2(1,1);
// for(int i=1;i<=n;++i) printf("Idx[%d]=%d\n",i,Idx[i]);
Seg.Build(1,n,1,B);
for(int i=1;i<=q;++i){
int a,b;scanf("%d%d",&a,&b);
printf("%d\n",QueryCnt(a,b)%MOD);
}
}