更新中.......
A.Alisha's Party (模拟+优先队列)
题意:Alisha 他邀请了她的朋友参加她的生日party,每个朋友将会给她带一份礼物。由于她的院子不够大,每次他的某个朋友来了之后她就会开门同时放p个人进来(假如外面没有p个人,就把外面的所有人放进来),进来的顺序按礼物的价值由搞到低进,价值相同的则按来的先后顺序进。当最后一个朋友来了之后,就会将所有没有进来的人全部放进去。现在问你q次,每次问你第x个进来的人是谁。
分析:用优先队列直接模拟就好了。注意最后要把所有的人放进来。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 2e5+6;
struct node
{
int v,id;
bool operator < (const node & t)const
{
if(v!=t.v)
return v<t.v;
return id>t.id;
}
}s[maxn];
char name[maxn][202];
pair <int ,int > p[maxn];
priority_queue <node > que;
int ans[maxn];
int main()
{
int ncase,n,k,m,i,j,x,y,q;
scanf("%d",&ncase);
while(ncase--)
{
scanf("%d%d%d",&k,&m,&q);
for(i=1;i<=k;i++)
{
scanf("%s%d",name[i],&s[i].v);
s[i].id=i;
}
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
p[i]=make_pair(x,y);
}
sort(p+1,p+m+1);
int cur=1,cnt=1,num=0;
for(i=1;i<=m;i++)
{
while(cur<=k && cur<=p[i].first)
que.push(s[cur++]);
num=p[i].second;
while(num-- && !que.empty())
{
ans[cnt++]=que.top().id;
que.pop();
}
}
while(cur<=k)
que.push(s[cur++]);
while(!que.empty())
{
ans[cnt++]=que.top().id;
que.pop();
}
scanf("%d",&x);
printf("%s",name[ans[x]]);
for(i=2;i<=q;i++)
{
scanf("%d",&x);
printf(" %s",name[ans[x]]);
}
printf("\n");
}
return 0;
}
B.Ponds(并查集)
题意:给定一个图,将图中所有度小于2的顶点去掉,知道图里面没有度小于2的顶点为止。求剩余的顶点的权值之和(注意题目要求剩余顶点所在的子图里面顶点数目为odd)。
分析:类似拓扑排序,每次去掉度为1的顶点,同时更新并查集。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 2e5+6;
struct node
{
int v,id;
bool operator < (const node & t)const
{
if(v!=t.v)
return v<t.v;
return id>t.id;
}
}s[maxn];
char name[maxn][202];
pair <int ,int > p[maxn];
priority_queue <node > que;
int ans[maxn];
int main()
{
int ncase,n,k,m,i,j,x,y,q;
scanf("%d",&ncase);
while(ncase--)
{
scanf("%d%d%d",&k,&m,&q);
for(i=1;i<=k;i++)
{
scanf("%s%d",name[i],&s[i].v);
s[i].id=i;
}
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
p[i]=make_pair(x,y);
}
sort(p+1,p+m+1);
int cur=1,cnt=1,num=0;
for(i=1;i<=m;i++)
{
while(cur<=k && cur<=p[i].first)
que.push(s[cur++]);
num=p[i].second;
while(num-- && !que.empty())
{
ans[cnt++]=que.top().id;
que.pop();
}
}
while(cur<=k)
que.push(s[cur++]);
while(!que.empty())
{
ans[cnt++]=que.top().id;
que.pop();
}
scanf("%d",&x);
printf("%s",name[ans[x]]);
for(i=2;i<=q;i++)
{
scanf("%d",&x);
printf(" %s",name[ans[x]]);
}
printf("\n");
}
return 0;
}
C.Aggregated Counting (OEIS+离线处理)
题意:给定序列a[1...oo]=1、2、2、3、3、4、4、4.......规律是加a[cur]个cur(cur表示当前值的大小)。比如再往后接,就是接3个5,再接4个6......,给定整数n,最后一个n的位置p1,最后一个p1的位置p2,求p2。比如n=3,最后一个3的位置是5,最后一个5的位置是11,那么答案就是11。
分析:先写个暴力版本的,把前面的几个数丢到OEIS.......发现有公式!!!公式:sigma{i*a[i],1<=i<=n},将和式压缩求解,比如n=3,ans=1*1+(2+3)*2。但是,复杂度为q*sqrt(n),数据比较多,发现计算小的对大的数有帮助,离线处理就好了。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1e6+6;
const LL mod = 1000000007;
int a[maxn],cur;
struct node
{
int id,x;
bool operator < (const node &t)const
{
return x<t.x;
}
}q[10000];
LL ans[10000];
void Init()
{
cur=3;
int p=4;
a[1]=1;
a[2]=2;
a[3]=2;
while(1)
{
for(int j=1;j<=a[cur] && p<maxn-1;j++)
a[p++]=cur;
cur++;
if(p>=maxn-1)
break;
}
}
int Find(int x)
{
int pos1=lower_bound(a,a+maxn,x+1)-a-1;
return lower_bound(a,a+maxn,pos1+1)-a-1;
}
inline LL cal(LL s,LL x)
{
return (s+s+x-1)*x/2%mod;
}
int getAns(int x)
{
LL cur=1,len=0,L=0;
LL ret=0;
while(L<x)
{
if(L+a[cur]<=x)
{
ret=(ret+cur*cal(L+1,a[cur]));
if(ret>mod)
ret%=mod;
L+=a[cur];
cur++;
}
else
{
ret=(ret+cur*cal(L+1,x-L));
if(ret>mod)
ret%=mod;
L=x;
}
}
//printf("cur:%lld \n",cur);
return int(ret);
}
void getall(int n)
{
LL cur=1,L=0;
LL ret=0;
for(int i=0;i<n;i++)
{
while(L<q[i].x)
{
if(L+a[cur]<q[i].x)
{
ret=(ret+cur*cal(L+1,a[cur]))%mod;
L+=a[cur];
cur++;
}
else
{
ans[q[i].id]=(ret+cur*cal(L+1,q[i].x-L))%mod;
break;
}
}
}
}
int main()
{
// freopen("test.txt","r",stdin);
// freopen("h.txt","w",stdout);
Init();
int ncase,x;
scanf("%d",&ncase);
for(int i=0;i<ncase;i++)
{
scanf("%d",&q[i].x);
q[i].id=i;
}
sort(q,q+ncase);
getall(ncase);
for(int i=0;i<ncase;i++)
printf("%lld\n",ans[i]);
return 0;
}
D.Clock Adjusting(待更新)
E.Travel(并查集+离线处理)
题意:给定一个图,有q次查询,每次查询给定一个整数x,问图里面有多少点对(u,v),(u,v)要满足:u<-->v这条路径上的相邻点的路径的权值不大于x。
分析;将边按权值由小到大排序。每查询一次,往图里面添加权值不大于x的边。每个子图就是一个集合,用并查集维护,动态更新答案。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 2e5+6;
int fa[maxn],sz[maxn];
LL ans[maxn];
struct node
{
int a,b,v;
bool operator < (const node &t)const
{
return v<t.v;
}
}s[maxn];
struct que
{
int id,x;
bool operator < (const que &t)const
{
return x<t.x;
}
}query[maxn];
int Find(int rt)
{
if(fa[rt]==rt)
return rt;
return fa[rt]=Find(fa[rt]);
}
inline LL cal(LL n)
{
if(n<2)
return 0;
return n*(n-1)>>1;
}
void Init(int lim)
{
for(int i=1;i<=lim;i++)
{
fa[i]=i;
sz[i]=1;
}
}
LL cur;
void update(int a,int b)
{
int root1=Find(a),root2=Find(b);
if(root1==root2)
return ;
cur-=cal(sz[root1])*2;
cur-=cal(sz[root2])*2;
fa[root1]=root2;
sz[root2]+=sz[root1];
sz[root1]=0;
cur+=cal(sz[root2])*2;
}
int main()
{
int ncase,n,m,i,j,x,y,v,q;
scanf("%d",&ncase);
while(ncase--)
{
scanf("%d%d%d",&n,&m,&q);
Init(n);
for(i=1;i<=m;i++)
scanf("%d%d%d",&s[i].a,&s[i].b,&s[i].v);
sort(s+1,s+m+1);
for(i=1;i<=q;i++)
{
scanf("%d",&query[i].x);
query[i].id=i;
}
sort(query+1,query+q+1);
j=1;
cur=0;
for(i=1;i<=q;i++)
{
for(;j<=m && s[j].v<=query[i].x;j++)
update(s[j].a,s[j].b);
ans[query[i].id]=cur;
}
for(i=1;i<=q;i++)
printf("%lld\n",ans[i]);
}
return 0;
}
F.Favorite Donut(后缀数组)
题意:给定长度为n的字符串s(s是环),然后以某一点顺时针或者逆时针出发遍历字符串s将得到一个t,求一个字典序最大的字符串t的起始位置。字典序相同的选起始位置靠前的,位置相同的选顺时针的。
分析:将原来的字符串添加字符,使得前n个字符,每个字符与其后面的n-1个字符正是循环遍历的字符串。比如aabca--->aabcaaabc (顺时针) aabca---->acbaaacba (逆时针),然后对新构造出来的字符串求其后缀数组,那么可以得到字典序最大的两个字符串(两个方向),把字符串取出来,然后比较一下就行了。
ps:对于顺时针的字符串末尾要加一个表示负无穷的字符。这样保证后缀suffix(i)与suffix(j)的LCP等于其中一个后缀的时候,位置靠前的优先选择。
对于逆时针的字符串末尾要加一个表示正无穷的字符。这样保证后缀suffix(i)与suffix(j)的LCP等于其中一个后缀的时候,位置靠后的优先选择。这里的靠后是反转之后的字符串,再反过来就是靠前的了。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
const int maxn = 1e5+6;
char in[maxn],s[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn],n;
void build_sa(int n,int m)
{
int i,*x=t,*y=t2;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]=s[i]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(i=n-k;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
if(p>=n) break;
m=p;
}
}
void GetSuffix(char buf[],char str[],int p,int n)
{
for(int cnt=0,i=p;cnt<n;cnt++,i++)
buf[cnt]=str[i];
buf[n]='\0';
}
char ts[maxn],s1[maxn],s2[maxn];
int main()
{
int ncase,n,i,j,p1,p2,len1,len2;
scanf("%d",&ncase);
while(ncase--)
{
scanf("%d%s",&n,in);
strcpy(ts,in);
for(i=0;i<n-1;i++)
ts[n+i]=ts[i];
ts[n+i]='\0';
len1=strlen(ts);
strcpy(s,ts);
build_sa(len1+1,255);
for(i=len1;i>=0 && sa[i]>=n;i--);p1=sa[i];
GetSuffix(s1,ts,p1,n);
strcpy(ts,in);
reverse(ts,ts+n);
for(i=0;i<n-1;i++)
ts[n+i]=ts[i];
ts[n+i]='\0';
len2=strlen(ts);
ts[len2]='z'+1;
strcpy(s,ts);
build_sa(len2+1,255);
for(i=len2;i>=0 && sa[i]>=n;i--) ;p2=sa[i];
GetSuffix(s2,ts,p2,n);
// printf("%s\n%s\n",s1,s2);
p1=p1+1;
p2=n-p2;
int temp=strcmp(s1,s2);
if(temp!=0)
temp==1?printf("%d 0\n",p1):printf("%d 1\n",p2);
else if(p1!=p2)
p1<p2?printf("%d 0\n",p1):printf("%d 1\n",p2);
else
printf("%d 0\n",p1);
}
return 0;
}
G.The Water Problem (暴力)
题意:查询区间最大值,数据范围很小。
分析:直接暴力查询就好,比赛的时候没看数据范围,用的线段树。。。。。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
int a[1005];
int main()
{
int ncase;
scanf("%d",&ncase);
while(ncase--)
{
int n,q,i,j,m,l,r;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
scanf("%d",&q);
while(q--)
{
m=-(1<<30);
scanf("%d%d",&l,&r);
for(i=l;i<=r;i++) if(a[i]>m)
m=a[i];
printf("%d\n",m);
}
}
return 0;
}
H. Elven Postman(二叉树)
题意:给定一棵排序二叉树的先序遍历,给定整数x,求从根节点到x的路径。
分析:暴力模拟。先把树建起来,然后由于是排序二叉树,直接走就行了。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1005;
struct node
{
int v;
node *lson,*rson;
node()
{
lson=rson=nullptr;
}
node(int x,node *l=nullptr,node *r=nullptr)
:v(x),lson(l),rson(r) {}
};
int a[maxn];
node* build(int x,int y)
{
if(x>y)
return nullptr;
if(x==y)
return new node(a[x]);
int cur=x+1;
while(cur<=y && a[cur]<a[x])
cur++;
node *L=build(x+1,cur-1);
node *R=build(cur,y);
return new node(a[x],L,R);
}
void check(node *root)
{
if(root!=nullptr)
{
check(root->lson);
printf("%d ",root->v);
check(root->rson);
}
}
void print(node *root,int x)
{
if(x==root->v)
return ;
if(x<root->v)
{
putchar('E');
print(root->lson,x);
}
else
{
putchar('W');
print(root->rson,x);
}
}
int main()
{
int ncase,n,q,i,j,x;
scanf("%d",&ncase);
while(ncase--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
node *root=build(1,n);
scanf("%d",&q);
while(q--)
{
scanf("%d",&x);
if(root->v==x)
{
puts("");
continue ;
}
print(root,x);
puts("");
}
}
return 0;
}
I.Food Problem(待更新)
J.Unknow Treasure(lucas+crt)
题意:求C(n,m)%M,M<10^18。M=p1*p2.....pk,(1<=k<=10)and (pi<100 000,pi is s prime)。
分析:直接用lucas求出C(n,m)%pi的值,然后用中国剩余定理把这些值合并。可惜啊,模版题不会,要不然当时就出线了。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int N =150000;
LL fac[N];
void init(LL p)
{
int i;
fac[0] =1;
for(i =1; i <= p; i++)
fac[i] = fac[i-1]*i % p;
}
LL mul(LL a,LL b,LL p)
{
if(a==0 || b==0)
return 0;
int fg=1;
if(a<0 && b<0)
{
a=-a;
b=-b;
}
else if(a<0 || b<0)
{
if(a<0)
a=-a;
else
b=-b;
fg=-1;
}
LL ret(0);
while(b)
{
if(b&1)
ret=(ret+a)%p;
a=(a<<1)%p;
b>>=1;
}
return ret*fg;
}
LL myPow(LL a,LL n,LL p)
{
LL ret=1;
while(n)
{
if(n&1)
ret=mul(ret,a,p);
n>>=1;
a=mul(a,a,p);
}
return ret;
}
LL C(LL n,LL m,LL p)
{
if(m > n) return 0;
return fac[n]*myPow(fac[m]*fac[n-m], p-2,p)%p;
}
LL Lucas(LL n, LL m,LL p)
{
if(m ==0) return 1;
else return (C(n%p, m%p,p)*Lucas(n/p, m/p,p))%p;
}
LL r[100],mo[100];
void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(!b)
{
d=a;
x=1;
y=0;
return ;
}
gcd(b,a%b,d,y,x);
y-=x*(a/b);
}
LL china(int n) //x=r[i] (mod m[i])
{
LL M=1;
LL i,Mi,x,y,d,ans=0;
for(i=1;i<=n;i++)
M*=mo[i];
for(i=1;i<=n;i++)
{
Mi=M/mo[i];
gcd(Mi,mo[i],d,x,y);
//printf("%lld %lld %lld \n",Mi,x,r[i]);
LL temp=mul(Mi,x,M);
temp=mul(temp,r[i],M);
ans=(ans+temp+M)%M;
// ans=(ans+Mi*x*r[i])%M;
}
return (ans+M)%M;
}
int main()
{
int t,k,i,j;
scanf("%d",&t);
while(t--)
{
LL n,m,p;
scanf("%lld%lld%d",&n,&m,&k);
for(i=1;i<=k;i++)
{
scanf("%lld",&p);
init(p);
r[i]=Lucas(n,m,p);
mo[i]=p;
}
printf("%lld\n",china(k));
}
return 0;
}
K.Good Numbers(待更新)
L.Marisa's Cake(待更新)
M.Robot Dog(待更新)