A题
线段树+欧拉降幂
题目要求的状态只有30种,所以对于每次操作,我们可以枚举所有的情况,计算他的转移方向,我们用lazy标记表示这个数操作完后的状态,打个懒标记
然后对于询问的时候,只要询问每个数的个数即可。对于快速幂的情况,普通快速幂因为太慢,所以考虑使用欧拉降幂来加速
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=2e5+10; const int mod=1e9+7; struct node{ int l,r; int sum[33]; int lazy[33]; }tr[N<<2]; int n,p; int cnt[33],num[33]; int a[N]; int phi; int eular(int n){ int ans = n; for(int i=2;i*i<=n;++i){ if(n%i == 0){ ans = ans/i * (i-1); while(n % i == 0){ n /= i; } } } if(n > 1) ans = ans / n * (n - 1); return ans; } int gcd(int a,int b){ return b?gcd(b,a%b):a; } int qmi(int a,int b){ ll ans=1; if(gcd(a,p) == 1){ b=b%phi; } else if(b>=phi){ b=b%phi+phi; } while(b>0){ if(b&1) ans=ans*a%p; a=a*a%p; b>>=1; } return ans; } void pushup(int u){ for(int i=0;i<p;i++){ tr[u].sum[i]=tr[u<<1].sum[i]+tr[u<<1|1].sum[i]; } } void build(int u,int l,int r){ if(l==r){ tr[u]={l,r}; for(int i=0;i<p;i++){ tr[u].lazy[i]=i; } tr[u].sum[a[l]%p]++; } else{ tr[u]={l,r}; for(int i=0;i<p;i++){ tr[u].lazy[i]=i; } int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); pushup(u); } } int get(int opt,int i,int l){ if(opt==1){ return 1ll*(i+l)%p; } else if(opt==2){ return 1ll*i*l%p; } else{ return 1ll*(qmi(i,l))%p; } } void pushdown(int u){ int i; for(i=0;i<p;i++){ num[i]=tr[u<<1].sum[i]; tr[u<<1].sum[i]=0; } for(i=0;i<p;i++){ tr[u<<1].sum[tr[u].lazy[i]]+=num[i]; } for(i=0;i<p;i++){ num[i]=tr[u<<1|1].sum[i]; tr[u<<1|1].sum[i]=0; } for(i=0;i<p;i++){ tr[u<<1|1].sum[tr[u].lazy[i]]+=num[i]; } for(i=0;i<p;i++){ tr[u<<1].lazy[i]=tr[u].lazy[tr[u<<1].lazy[i]]; tr[u<<1|1].lazy[i]=tr[u].lazy[tr[u<<1|1].lazy[i]]; } for(i=0;i<p;i++){ tr[u].lazy[i]=i; } } void modify(int u,int l,int r,int x,int opt){ if(tr[u].l>=l&&tr[u].r<=r){ int i; for(i=0;i<p;i++){ num[i]=tr[u].sum[i]; tr[u].sum[i]=0; } for(i=0;i<p;i++){ int pos=get(opt,i,x); tr[u].lazy[i]=get(opt,tr[u].lazy[i],x); tr[u].sum[pos]+=num[i]; } return ; } pushdown(u); int mid=tr[u].l+tr[u].r>>1; if(l<=mid) modify(u<<1,l,r,x,opt); if(r>mid) modify(u<<1|1,l,r,x,opt); pushup(u); } void query(int u,int l,int r){ if(tr[u].l>=l&&tr[u].r<=r){ int i; for(i=0;i<p;i++){ cnt[i]+=tr[u].sum[i]; } return ; } pushdown(u); int mid=tr[u].l+tr[u].r>>1; if(l<=mid) query(u<<1,l,r); if(r>mid) query(u<<1|1,l,r); } int main(){ ios::sync_with_stdio(false); int i; cin>>n>>p; phi=eular(p); for(i=1;i<=n;i++) cin>>a[i]; build(1,1,n); int q; cin>>q; while(q--){ int opt,l,r,k; cin>>opt>>l>>r>>k; if(opt<=3){ modify(1,l,r,k,opt); } else if(opt==4){ memset(cnt,0,sizeof cnt); int ans=0; query(1,l,r); for(i=0;i<p;i++){ ans=(ans+qmi(i,k)*cnt[i])%p; } cout<<ans<<endl; } else{ memset(cnt,0,sizeof cnt); int ans=1; query(1,l,r); for(i=0;i<p;i++){ ans=ans*qmi(i,cnt[i])%p; } cout<<ans<<endl; } } return 0; }
C题
观察到,对于每个数,都放置在比他小1的数的两边就是一种 合法方案,那么这种方案其实就是二叉树的中序遍历
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=5e6+10; const int mod=1e9+7; int l[N],r[N],w[N]; int cnt; vector<int> num; void dfs(int u,int sum){ w[u]=sum; if(sum==20) return ; l[u]=u<<1,r[u]=u<<1|1; dfs(u<<1,sum+1); dfs(u<<1|1,sum+1); } void get(int u){ if(l[u]) get(l[u]); num.push_back(w[u]); if(r[u]) get(r[u]); } int main(){ ios::sync_with_stdio(false); int n; cin>>n; int i; dfs(1,1); get(1); for(i=1;i<=n;i++) cout<<num[i]<<" "; cout<<endl; }
D题
线段树+思维
首先要想到小范围的答案不会影响大范围,因此考虑先对操作离线排序。对于下一步
我们想到,如果直到这个数在第几组删除,并且是这个组的第几个那么就能知道全部答案
这步操作,可以通过线段树维护,因为每个数只有变成1或者变成当前位置是质数的时候。
这样只要维护每一轮删除的数,之后从第0轮往上增即可,因为轮数不会太多,所以复杂度过关
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=1e6+10; const int mod=1e9+7; struct node{ int l,r; int sum; }tr[N<<2]; struct query{ int op,n,k,id; }s[N]; int num[N],pos[N]; vector<int> g[N]; int st[N],primes[N]; int ans[N]; bool cmp(query a,query b){ return a.n<b.n; } void build(int u,int l,int r){ if(l==r){ tr[u]={l,r}; } else{ tr[u]={l,r}; int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); } } void pushup(int u){ tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum; } void modify(int u,int l,int r,int p,int x){ if(tr[u].l==tr[u].r){ tr[u].sum+=x; return ; } int mid=tr[u].l+tr[u].r>>1; if(p<=mid) modify(u<<1,l,r,p,x); else modify(u<<1|1,l,r,p,x); pushup(u); } int query(int u,int l,int r,int x){ if(x==0) return 0; if(tr[u].l==tr[u].r){ return tr[u].sum; } int mid=tr[u].l+tr[u].r>>1; if(x<=mid){ return query(u<<1,l,r,x); } else{ return tr[u<<1].sum+query(u<<1|1,l,r,x); } } int get(int u,int l,int r,int x){ if(tr[u].l==tr[u].r){ return tr[u].l; } int mid=tr[u].l+tr[u].r>>1; if(x<=tr[u<<1].sum){ return get(u<<1,l,r,x); } else{ return get(u<<1|1,l,r,x-tr[u<<1].sum); } } int cnt; void init(int mx){ int i; for(i=2;i<=mx;i++){ if(!st[i]){ primes[++cnt]=i; } for(int j=1;primes[j]*i<=mx;j++){ st[i*primes[j]]=1; if(i%primes[j]==0){ break; } } } } int main(){ ios::sync_with_stdio(false); int t; cin>>t; int i; int mx=0; for(i=1;i<=t;i++){ cin>>s[i].op>>s[i].n>>s[i].k; s[i].id=i; mx=max(mx,s[i].n); } sort(s+1,s+1+t,cmp); int now=1; build(1,1,1000); init(mx); for(i=1;i<=mx;i++){ if(i==1){ modify(1,1,1000,1,1); num[i]=1; pos[i]=1; g[1].push_back(1); } else{ int turn=0; while(1){ int tmp=query(1,1,1000,turn); if(i-tmp==1||!st[i-tmp]){ break; } turn++; } num[i]=turn+1; modify(1,1,1000,turn+1,1); g[turn+1].push_back(i); pos[i]=g[turn+1].size(); } while(now<=t&&s[now].n==i){ if(s[now].op==1){ int tmpsum=query(1,1,1000,num[s[now].k]-1); tmpsum+=pos[s[now].k]; ans[s[now].id]=tmpsum; } else{ int tmpsum=get(1,1,1000,s[now].k); int res=s[now].k-query(1,1,1000,tmpsum-1); ans[s[now].id]=g[tmpsum][res-1]; } now++; } } for(i=1;i<=t;i++) cout<<ans[i]<<endl; return 0; }
H题
简单dp题,因为发现数据不会很大,因此直接暴力dp匹配即可,注意判断可能模完后变成0和1的情况
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; typedef pair<string,int> pss; const int N=1e5+10; const int mod=1e9+7; ll f[N]; map<string,int> m1; string s; ll f1[N]; int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ m1.clear(); memset(f,0,sizeof f); memset(f1,0,sizeof f1); int n,m; cin>>n>>m; for(int i=1;i<=m;i++){ string a,b; cin>>a>>b; m1[b]++; } cin>>s; s=" "+s; f[0]=1; f1[0]=1; for(int i=1;i<=n;i++){ for(int j=1;j<=5&&j<=i;j++){ string d=s.substr(i-j+1,j); if(m1[d]){ f[i]=(f[i]+f[i-j]*m1[d]%128)%128; f1[i]=(f1[i]+f1[i-j]*m1[d]%mod)%mod; } } } if(f[n]==1&&f1[n]==1){ cout<<"happymorsecode"<<endl; } else if(!f[n]&&!f1[n]){ cout<<"nonono"<<endl; } else{ cout<<"puppymousecat "<<f[n]<<endl; } } }
I题
最短路+思维
注意到这个图是边权不断在变的正权图,因此依旧满足迪杰斯特拉的贪心法则。
所以我们依旧跑最短路,这依然是正解,只不过要根据时间加两条不同的边。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; typedef pair<string,int> pss; const int N=1e5+10; const int mod=1e9+7; int sx,sy,ex,ey; int n,m; ll a[505][505],b[505][505]; ll c[505][505],d[505][505]; ll dis[5050][505]; int st[505][505]; struct node{ int x,y; ll dis; bool operator <(const node &t) const{ return dis>t.dis; } }; void dij(){ memset(dis,0x3f,sizeof dis); dis[sx][sy]=0; priority_queue<node> q; q.push({sx,sy,0}); while(q.size()){ auto t=q.top(); q.pop(); if(st[t.x][t.y]) continue; st[t.x][t.y]=1; ll lim=t.dis%(a[t.x][t.y]+b[t.x][t.y]); int x=t.x,y=t.y; if(x>1){ if(lim<a[x][y]){ if(dis[x-1][y]>dis[x][y]+d[x-1][y]){ dis[x-1][y]=dis[x][y]+d[x-1][y]; q.push({x-1,y,dis[x-1][y]}); } } else{ ll tmp=a[x][y]+b[x][y]-lim; if(dis[x-1][y]>dis[x][y]+d[x-1][y]+tmp){ dis[x-1][y]=dis[x][y]+d[x-1][y]+tmp; q.push({x-1,y,dis[x-1][y]}); } } } if(x<n){ if(lim<a[x][y]){ if(dis[x+1][y]>dis[x][y]+d[x][y]){ dis[x+1][y]=dis[x][y]+d[x][y]; q.push({x+1,y,dis[x+1][y]}); } } else{ ll tmp=a[x][y]+b[x][y]-lim; if(dis[x+1][y]>dis[x][y]+d[x][y]+tmp){ dis[x+1][y]=dis[x][y]+d[x][y]+tmp; q.push({x+1,y,dis[x+1][y]}); } } } if(y>1){ if(lim>=a[x][y]){ if(dis[x][y-1]>dis[x][y]+c[x][y-1]){ dis[x][y-1]=dis[x][y]+c[x][y-1]; q.push({x,y-1,dis[x][y-1]}); } } else{ ll tmp=a[x][y]-lim; if(dis[x][y-1]>dis[x][y]+c[x][y-1]+tmp){ dis[x][y-1]=dis[x][y]+c[x][y-1]+tmp; q.push({x,y-1,dis[x][y-1]}); } } } if(y<m){ if(lim>=a[x][y]){ //cout<<"gg"<<endl; if(dis[x][y+1]>dis[x][y]+c[x][y]){ //cout<<"hh"<<endl; dis[x][y+1]=dis[x][y]+c[x][y]; q.push({x,y+1,dis[x][y+1]}); } } else{ ll tmp=a[x][y]-lim; if(dis[x][y+1]>dis[x][y]+c[x][y]+tmp){ dis[x][y+1]=dis[x][y]+c[x][y]+tmp; q.push({x,y+1,dis[x][y+1]}); } } } } cout<<dis[ex][ey]<<endl; } int main(){ ios::sync_with_stdio(false); cin>>n>>m>>sx>>sy>>ex>>ey; int i,j; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ cin>>a[i][j]; } } for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ cin>>b[i][j]; } } for(i=1;i<=n;i++){ for(j=1;j<m;j++) cin>>c[i][j]; } for(i=1;i<n;i++){ for(j=1;j<=m;j++) cin>>d[i][j]; } dij(); }
J题
队友写的
#include<bits/stdc++.h> using namespace std; #define ll long long #define LL long long #define ull unsigned long long #define PI acos(-1.0) #define eps 1e-12 #define fi first #define se second #define MEM(a,b) memset((a),(b),sizeof(a)) #define mod(x) ((x)%MOD) #define wz cout<<"-----"<<endl; #define pb push_back #define mp make_pair #define pll pair <LL, LL> #define pii pair <int, int> #define rep(i,x) for(int i=1;i<=x;i++) const int INF_INT = 2147483647; const ll INF_LL = 9223372036854775807LL; const ull INF_ULL = 18446744073709551615Ull; const ll P = 92540646808111039LL; const ll maxn = 1e5 + 10, MOD = 1e9 + 7; const int Move[4][2] = {-1,0,1,0,0,1,0,-1}; const int Move_[8][2] = {-1,-1,-1,0,-1,1,0,-1,0,1,1,-1,1,0,1,1}; inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int tx[1000033]; int y; int g(int x){ if(tx[x])return tx[x]; else return tx[x]=1+g(y%x); } int main(){ int t=read(); while(t--){ y=read(); if(y==2){ printf("%.10lf ",1.0); continue; } ll sum=0; tx[1]=1; for(int i=1;i<=y/2;i++){ sum+=g(i); } sum=2*sum+y/2; printf("%.10lf ",sum*1.0/(y-1)); for(int i=1;i<y;i++)tx[i]=0; } }