链接:https://www.jisuanke.com/contest/3005?view=challenges
A. Who is better?
拓展欧几里得+斐波那契博弈
#include<algorithm>
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll f[80]={1,1};
ll ex_gcd(ll a,ll b,ll& x,ll& y)
{
if(b==0)
{
x=1,y=0;
return a;
}
ll r,tx,ty;
r=ex_gcd(b,a%b,tx,ty);
x=ty;
y=tx-a/b*ty;
return r;
}
void pre()
{
for(int i=2;i<80;i++) f[i]=f[i-1]+f[i-2];
}
int main()
{
pre();
ll i,n,a1,r1,a2,r2,ans,a,b,c,d,x0,y0;
scanf("%lld",&n);
bool flag = 1;
scanf("%lld%lld",&a1,&r1);
for( i=1;i<n;i++){
scanf("%lld%lld",&a2,&r2);
a = a1;
b = a2;
c = r2-r1;
ll d = ex_gcd(a,b,x0,y0);
if(c%d!=0){
flag = 0;
}
int t = b/d;
//printf("%lld\n",d);
//printf("%lld\n",x0);
x0 = (x0*(c/d)%t+t)%t;//保证x0为正
r1 = a1*x0 + r1;
a1 = a1*(a2/d);
}
if(!flag){
puts("Tankernb!");
return 0;
}
if(r1>1e15){
puts("Tankernb!");
return 0;
}
//printf("%lld\n",r1);
bool ff=0;
for(int j=1;j<80;j++)
{
if(f[j]==r1)
{
ff=1;
break;
}
}
if(ff) cout<<"Lbnb!"<<endl;
else cout<<"Zgxnb!"<<endl;
return 0;
}
B. so easy
并查集离散化
用unordered_map会炸
#include<bits/stdc++.h>
#define MAXN 2000005
#define ll long long
using namespace std;
int b[MAXN];
int val[MAXN];
int f[MAXN];
int get(int x)
{
if(f[x]!=x)
f[x]=get(f[x]);
return f[x];
}
void init()
{
for(int i=0;i<MAXN;i++)
f[i]=i;
}
int x[MAXN],y[MAXN];
int main()
{
init();
int n,m;
scanf("%d%d",&n,&m);
int num=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x[i],&y[i]);
b[++num]=y[i];
b[++num]=y[i]+1;
}
sort(b+1,b+num+1);
num=unique(b+1,b+num+1)-b-1;
for(int i=1;i<=m;i++)
{
int t=y[i];
y[i]=lower_bound(b+1,b+num+1,y[i])-b;
val[y[i]]=t;
val[y[i]+1]=t+1;
}
for(int i=1;i<=m;i++)
{
if(x[i]==1)
{
int xx=get(y[i]);
int yy=get(y[i]+1);
f[xx]=yy;
}
else{
int cnt=val[get(y[i])];
if(cnt==n+1)
printf("-1\n");
else
printf("%d\n",cnt);
}
}
return 0;
}
C. Buy Watermelon
签到,>=4&&偶数
D. Carneginon
kmp搞搞,或者模拟
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int Next[100010];
string M,N;
void getNext()
{
int len=M.length();
Next[0]=-1;
int i=0,j=-1;
while(i<len)
{
if(j==-1||M[i]==M[j])
Next[++i]=++j;
else j=Next[j];
}
}
int Kmp()
{
int len1=M.length(),len2=N.length();
int cnt=0,i=-1,j=-1;
while(j<len2)
{
if(i==-1||M[i]==N[j])
{
i++,j++;
if(i==len1)
{
cnt++;
}
}
else i=Next[i];
}
return cnt;
}
int main()
{
string T,S;
int t;
cin>>T>>t;
int n=T.length();
while(t--)
{
cin>>S;
int m=S.length();
if(m==n)
{
if(T==S) cout<<"jntm!"<<endl;
else cout<<"friend!"<<endl;
}
else if(m>n)
{
M=T;N=S;
getNext();
if(Kmp()) cout<<"my teacher!"<<endl;
else cout<<"senior!"<<endl;
}
else
{
M=S;N=T;
getNext();
if(Kmp()) cout<<"my child!"<<endl;
else cout<<"oh, child!"<<endl;
}
}
return 0;
}
E. XKC's basketball team
预处理从后往前的最大值,然后二分
#include<bits/stdc++.h>
#define ll long long
#define MAXN 500005
using namespace std;
int maxs[MAXN],a[MAXN];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=n;i>=1;i--)
maxs[i]=max(maxs[i+1],a[i]);
for(int i=1;i<=n;i++)
{
int l=i+1,r=n;
int ans=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(a[i]+m<=maxs[mid])
{
l=mid+1;
ans=max(ans,mid);
}
else{
r=mid-1;
}
}
if(ans==-1)
printf("-1");
else
printf("%d",ans-i-1);
if(i!=n)
printf(" ");
}
printf("\n");
return 0;
}
G. Colorful String
回文树
回文树:
其实可以说是两棵树,一棵是奇树、一棵是偶树。而回文树中
nex数组:指向的串为当前串两端加上同一个字符构成
fail数组:fail跳转到自己这个串的最长回文后缀
实线为nex指向节点。虚线为fail指向节点
用深搜搜索这棵树,遍历每个子串的情况
#include<bits/stdc++.h>
#define ll long long
#define sigma_size 30
#define MAXN 600005
using namespace std;
char p[MAXN];
struct PAM{
ll ans=0;
int siz[30];
int nex[MAXN][sigma_size]; //字符表的大小
int fail[MAXN];
int cnt[MAXN]; // 节点i表示的回文串在S中出现的次数(建树时求出的不是完全的,count()加上子节点以后才是正确的)
int num[MAXN]; //以节点i回文串的末尾字符结尾的但不包含本条路径上的回文串的数目。(也就是fail指针路径的深度)
int len[MAXN]; //节点i的回文串的长度 2~p-1()
int S[MAXN]; //表示第i次添加的字符 // 2~p-1
int last,n,p; //p-2是不同回文串个数
//last指向最新添加的回文结点
int newnode(int rt){//新建节点
memset(nex[p],0,sizeof(nex[p]));
cnt[p]=0;
num[p]=0;
len[p]=rt;
return p++;
}
void init(){//初始化
ans=0;
p=last=n=0;
newnode(0);
newnode(-1);
S[0]=-1;
fail[0]=1;
}
int getFail(int x){//寻找失败节点
while(S[n-len[x]-1]!=S[n]) x=fail[x];
return x;
}
void add(int c){//插入字符,看题目要求
c=c-'a';
S[++n]=c;
int cur=getFail(last);
if(!nex[cur][c]){
int now=newnode(len[cur]+2);
fail[now]=nex[getFail(fail[cur])][c];
nex[cur][c]=now;
num[now]=num[fail[now]]+1;
}
last=nex[cur][c];
cnt[last]++;
}
void count1()//求每个回文子串的个数
{
for (int i = p-1; i >= 0; i--)
cnt[ fail[i] ] += cnt[i];
}
void dfs(int rt,int s)//根,个数
{
for(int i=0;i<26;i++)//遍历扩展的每一种回文子串的情况
{
if(!nex[rt][i])
continue;
if(siz[i]){
ll ts=s;
ans+=(ts*cnt[nex[rt][i]]);//本质不同的回文子串数目*不同字母个数
dfs(nex[rt][i],ts);
}
else{
siz[i]++;
ll ts=s+1;
ans+=(ts*cnt[nex[rt][i]]);
dfs(nex[rt][i],ts);
siz[i]--;
}
}
}
void solve(char str[])
{
init();
int Size=strlen(str);
for(int i=0;i<Size;i++)
add(str[i]);
count1();//计算本质不同回文子串的个数
dfs(0,0);//偶根
memset(siz,0,sizeof(siz));
dfs(1,0);//奇根
printf("%lld\n",ans);
}
}pam;
int main()
{
pam.init();
cin>>p;
pam.solve(p);
return 0;
}
I. query
树状数组离线询问,单调递增插每个a[i]的贡献.
我们查询分两个,一个按l排序,一个按r排序,用l排序的为了处理(1,l-1)对(l,r)产生的影响
for(i->n),边查边插入,每到一个i点,减掉左端点在这里的区间和, 去除(1,l-1)对(l,r)产生的影响,查询右端点在i处的区间和(这里已经处理了左端点).
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) (x)&(-x)
#define MAXN 200005
using namespace std;
int a[MAXN],pos[MAXN];
int sum[MAXN];
int ans[MAXN];
int n;
struct node
{
int l,r,id;
}aa[MAXN],bb[MAXN];
bool aac(node a,node b)
{
return a.l<b.l;
}
bool bbc(node a,node b)
{
return a.r<b.r;
}
void add(int x,int val)
{
for(int i=x;i<=n;i+=lowbit(i))
sum[i]+=val;
}
ll query(int x)
{
ll ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=sum[i];
return ans;
}
int main()
{
int m,l,r;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[a[i]]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&l,&r);
if(l>r)swap(l,r);
aa[i]=bb[i]=node{l,r,i};
}
sort(aa+1,aa+m+1,aac);
sort(bb+1,bb+m+1,bbc);
for(int i=1,j=1,k=1;i<=n;i++)
{
while(j<=m&&aa[j].l==i)
ans[aa[j].id]-=query(aa[j].r)-query(aa[j].l-1),j++;
for(int p=a[i];p<=n;p+=a[i])
add(pos[p],1);
while(k<=m&&bb[k].r==i)
ans[bb[k].id]+=query(bb[k].r)-query(bb[k].l-1)-(bb[k].r-bb[k].l+1),k++;
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
K. Center
对称点必然是一些线的中点
每个点之间相连,会得到一些中点,与其他点计算+2,与自己计算+1(自己就是中点)
遍历每一种情况,看最多省多少点
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<map>
#define ll long long
using namespace std;
struct node{
int x,y;
}a[1010];
map<pair<int,int>,int> mp;
int n;
int main()
{
mp.clear();
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d%d",&a[i].x,&a[i].y);
for(int i=0;i<n;i++)
{
mp[make_pair(a[i].x*2,a[i].y*2)]+=1;
for(int j=1+i;j<n;j++)
{
int cx=a[i].x+a[j].x;
int cy=a[i].y+a[j].y;
mp[make_pair(cx,cy)]+=2;
}
}
int ansx,ansy,ansc=0;
for(map<pair<int,int>,int>:: iterator it=mp.begin();it!=mp.end();it++)
{
if(it->second>ansc)
{
ansc=it->second;
ansx=it->first.first;
ansy=it->first.second;
}
}
//cout<<ansx<<' '<<ansy<<endl;
cout<<n-mp[make_pair(ansx,ansy)]<<endl;
return 0;
}
M. Longest subsequence
模拟,注意剪枝
#include<bits/stdc++.h>
#define MAXN 1000005
#define ll long long
using namespace std;
char pp[MAXN],ss[MAXN];
int nt[MAXN][26];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
scanf("%s%s",pp+1,ss+1);
for(int i=n;i>=1;i--){
for(int j=0;j<26;j++){
nt[i-1][j]=nt[i][j];
}
nt[i-1][pp[i]-'a']=i;
}
int ans=0,st=0;
for(int i=1;i<=m;i++)
{
int c=ss[i]-'a';
for(int j=c+1;j<26;j++)
{
int v=nt[st][j];
if(v==0)
continue;
ans=max(ans,i-1+(n-v+1));
}
int g=nt[st][c];
if(g==0){
break;
}
else{
if(i==m&&g<n){
ans=max(ans,i+(n-g));
break;
}
st=g;
}
}
if(ans==0)
printf("-1\n");
else
printf("%d\n",ans);
return 0;
}