A - Water The Garden
/*
给出一排格子,告诉你一些格子上有水,会往两边流动,问流满全部格子需要的时间
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
int main(){
int T,n,m,a[210],d[210],ans;
scanf("%d",&T);
while(T--){
ans=0;
scanf("%d%d",&n,&m);
memset(d,0x3f,sizeof(d));
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
d[i]=min(d[i],abs(i-a[j])+1);
}ans=max(ans,d[i]);
}
printf("%d
",ans);
}return 0;
}
B - Tea Queue
/*
告诉你n个人来打水的时间和等待到的最迟时间,来的时间相同的话编号小的排前面
问每个人取到水的时间,如果没取到水,则输出0
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=5010;
int T,n,l[N],r[N],ans[N];
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&l[i],&r[i]);
int nowt=0;
for(int i=1;i<=n;i++){
if(nowt>r[i]){ans[i]=0;continue;}
if(l[i]>=nowt)ans[i]=l[i],nowt=l[i]+1;
else ans[i]=nowt,nowt++;
}for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'
':' ');
}return 0;
}
C - Swap Adjacent Elements
/*
告诉你一个a序列,和一个s序列,s序列为1表示这个位置的a序列可以和下一个互换,
问能否通过随意交换可互换元素将a序列排序
*/
#include <cstdio>
#include <algorithm>
using namespace std;
int n,a[200010];
char s[200010];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
scanf("%s",s+1);s[n]='0';
for(int i=1;i<n;i++){
if(s[i]=='1'){
for(int j=i;j<=n;j++){
if(s[j]=='0'){
sort(a+i,a+j+1);
i=j;
break;
}
}
}
}
int flag=1;
for(int i=1;i<n;i++){
if(a[i]>a[i+1]){puts("NO");flag=0;break;}
}if(flag)puts("YES");
return 0;
}
D - Tanks
/*
题目大意:给出N个容量无限的桶,每个桶中开始都有一些水,给出一个大小为K的勺子,
问你能否通过N+5次以内的勺水操作(从一个桶到另一个桶),使得最后能有一个桶中的
水为V
题解:我们将所有的桶中的水量对K取模,如果这些值能够取一些相加取模后得到V%K,
说明答案可行,我们可以用01背包解决这个问题,对于路径输出,我们在做dp的时候记录路径,
表示这个位置是答案的组成部分,那么根据是否是答案的组成部分,我们将桶分为两部分,
对于处于同个部分的桶,我们把水全都倒进一个桶中,
那么最后只要在这两个桶相互倒就可以得到答案
*/
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN=5010;
int N,K,V,S,a[MAXN];
bool dp[MAXN][MAXN],pre[MAXN][MAXN];
vector<int> vc[2];
void Solve(){
for(int i=1;i<=N;i++)S+=a[i];
if(S<V){puts("NO");return;}
dp[0][0]=1;
for(int i=1;i<=N;i++){
int x=a[i]%K;
for(int j=0;j<K;j++)if(dp[i-1][j]){
dp[i][j]=1; pre[i][j]=0;
dp[i][(j+x)%K]=1; pre[i][(j+x)%K]=1;
}
}if(!dp[N][V%K]){puts("NO");return;}
int v=V%K;
for(int i=N;i>0;i--){
int x=a[i]%K;
if(pre[i][v]){
vc[1].push_back(i);
v=(v-x+K)%K;
}else vc[0].push_back(i);
}
puts("YES");
sort(vc[0].begin(),vc[0].end());
sort(vc[1].begin(),vc[1].end());
for(int k=0;k<2;k++)if(vc[k].size()){
for(int i=1;i<vc[k].size();i++)printf("%d %d %d
",1000000000,vc[k][i],vc[k][0]);
}
int x0,x1,s=0;
for(int i=0;i<vc[1].size();i++)s+=a[vc[1][i]];
if(!vc[0].size())x1=vc[1][0],x0=vc[1][1];
else if(!vc[1].size())x1=vc[0][1],x0=vc[0][0];
else x1=vc[1][0],x0=vc[0][0];
if(s<V)printf("%d %d %d
",(V-s)/K,x0,x1);
if(s>V)printf("%d %d %d
",(s-V)/K,x1,x0);
}
int main(){
scanf("%d%d%d",&N,&K,&V);
for(int i=1;i<=N;i++)scanf("%d",&a[i]);
Solve();
return 0;
}
E - Connected Components?
/*
题目大意:给出一张图,求补图的连通块和其大小
题解:搜索的理论复杂度O(n^2),显然不行,
bfs的时候对于已经归入其余连通块的点用并查集进行段无效信息处理,减少搜索树的分支,
显然经过这样的处理搜索分支的数量下降得非常快,就能顺利解决此题了
*/
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int N=200010;
int f[N],n,m,vis[N],ans,cnt[N];
vector<int> v[N];
int sf(int x){return f[x]==x?x:f[x]=sf(f[x]);}
void bfs(int st){
queue<int> q;
q.push(st);
cnt[++ans]=1;
while(q.size()){
int x=q.front();q.pop();f[x]=sf(x+1);
for(int i=0;i<v[x].size();i++)vis[v[x][i]]=x;
for(int i=sf(1);i<=n;i=sf(i+1))if(vis[i]!=x){
cnt[ans]++;
f[i]=sf(i+1);
q.push(i);
}
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
ans=0;
while(m--){
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
for(int i=1;i<=n+1;i++)f[i]=i;
for(int i=1;i<=n;i=sf(i+1))bfs(i);
printf("%d
",ans);
sort(cnt+1,cnt+ans+1);
for(int i=1;i<=ans;i++)printf("%d%c",cnt[i],i==ans?'
':' ');
}return 0;
}
F - SUM and REPLACE
/*
题目大意:给出一个序列,要求维护区间求和,和区间修改操作,
修改操作是将区间中每个数变为其约数个数
题解:我们计算一下约数个数发现,D(1)=1,D(2)=2,如果一个区间全是这样的数字的话
我们就不需要对这个区间进行修改,所以我们只要暴力改数值,当发现一个区间的最大值是1或2时
我们就停止往下修改
*/
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=1000010,M=2000010;
int p[N];
namespace Segment_Tree{
int tot;
struct node{int l,r,a,b;LL mx,val;}T[M];
void build(int,int);
void Initialize(int n){
tot=0;
build(1,n);
}
void up(int x){
T[x].val=T[T[x].l].val+T[T[x].r].val;
T[x].mx=max(T[T[x].l].mx,T[T[x].r].mx);
}
void build(int l,int r){
int x=++tot;
T[x].a=l;T[x].b=r;T[x].l=T[x].r=T[x].val=T[x].mx=0;
if(l==r){scanf("%lld",&T[x].val);T[x].mx=T[x].val;return;}
int mid=(l+r)>>1;
T[x].l=tot+1;build(l,mid);
T[x].r=tot+1;build(mid+1,r);
up(x);
}
void change(int x,int a,int b){
if(T[x].mx==1||T[x].mx==2)return;
if(T[x].a==T[x].b){T[x].mx=T[x].val=p[T[x].val];return;}
int mid=(T[x].a+T[x].b)>>1;
if(mid>=a&&T[x].l)change(T[x].l,a,b);
if(mid<b&&T[x].r)change(T[x].r,a,b);
up(x);
}
LL query(int x,int a,int b){
if(T[x].a>=a&&T[x].b<=b)return T[x].val;
int mid=(T[x].a+T[x].b)>>1; LL res=0;
if(mid>=a&&T[x].l)res+=query(T[x].l,a,b);
if(mid<b&&T[x].r)res+=query(T[x].r,a,b);
return res;
}
}
int n,m,t,l,r;
int main(){
for(int i=1;i<=1000000;i++){
for(int j=i;j<=1000000;j+=i)p[j]++;
}
scanf("%d%d",&n,&m);
Segment_Tree::Initialize(n);
while(m--){
scanf("%d%d%d",&t,&l,&r);
if(t==1)Segment_Tree::change(1,l,r);
else printf("%lld
",Segment_Tree::query(1,l,r));
}return 0;
}
G - List Of Integers
/*
题目大意:求出比x大的和p互质的第k大的数
题解:我们二分答案,将题目转化为判定区间内和一个数互质的数是否大于k
求区间内和一个数互质的数的个数我们可以用容斥来解决,将这个数的约数筛选出来,
然后容斥区间内约数个数出现的次数即可
*/
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=10000010;
typedef long long LL;
#define pw(x) (1LL<<x)
int T;
LL x,y,k,p;
LL cal(LL x,vector<LL> &p){
LL res=0;
int n=p.size();
for(int i=0;i<pw(n);i++){
LL t=1,f=1;
for(int j=0;j<n;j++)if(i>>j&1)t*=p[j],f*=-1;
res+=f*(x/t);
}return res;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%lld%lld%lld",&x,&p,&k);
vector<LL> f;
for(LL i=2;i*i<=p;i++)if(p%i==0){
f.push_back(i);
while(p%i==0)p/=i;
}
if(p>1)f.push_back(p);
k+=cal(x,f);
LL l=1LL,r=1000000000000LL,ans;
while(l<=r){
LL mid=(l+r)/2;
if(cal(mid,f)>=k)ans=mid,r=mid-1;
else l=mid+1;
}printf("%lld
",ans);
}return 0;
}