HZNU Personal Training
B - Infinite Prefixes
做出前缀a [ i ] ,那么对于每一个成立的情况 : a [ i ] + k * d = x
一遍枚举,特判 - 1 和 0的情况。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+500; ll a[N]; int main(){ int t;cin>>t; while(t--){ ll n,x;cin>>n>>x; string s;cin>>s; ll cur=0,cnt=0; bool flag=0; for(int i=0;i<n;i++){ if(s[i]=='0')cur++; else cur--; a[i]=cur; if(a[i]==x)flag=1; } ll d=a[n-1]; if(x==0)cnt++; if(d==0&&(flag||x==0)){ puts("-1"); continue; } else if(d==0&&!flag){ cout<<cnt<<endl; continue; } for(int i=0;i<n;i++){ if((x-a[i])%d==0&&(x-a[i])/d>=0)cnt++; } cout<<cnt<<endl; } // system("pause"); return 0; }
C - Obtain The String
两个字符串,每次从 s 拿出一个子串,问几步构成 t ,
只有26个字母,标记出每个字母出现的下标,每一步记录当前下标,枚举下一个字母出现下标是否在当前下标的后面,二分处理。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+500; int main(){ int t;cin>>t; while(t--){ string a,b; cin>>a>>b; int lena=a.size(),lenb=b.size(); vector<int>v[30]; for(int i=0;i<lena;i++){ v[ a[i]-'a'].push_back(i); } bool check=1; for(int i=0;i<lenb;i++){ if(v[b[i]-'a'].empty()){ check=0; break; } } if(!check){ puts("-1"); continue; } for(int i=0;i<26;i++)sort(v[i].begin(),v[i].end()); int pos=v[ b[0]-'a' ][0],tot=0; int ans=1; while(tot<lenb){ bool flag=0; if(tot==lenb-1)break; // for(int i=0;i<v[b[tot+1]-'a'].size();i++){ // if(v[b[tot+1]-'a'][i]>pos){ // pos=v[b[tot+1]-'a'][i]; // tot++; // flag=1; // break; // } // } vector<int>::iterator it; it=upper_bound(v[b[tot+1]-'a'].begin(),v[b[tot+1]-'a'].end(),pos); if(it!=v[b[tot+1]-'a'].end()){ flag=1; pos=*it; tot++; } if(!flag){ ans++; pos=v[b[tot+1]-'a'][0];tot++; } } cout<<ans<<endl; } // system("pause"); return 0; }
D - Count Triangles
A≤x≤B≤y≤C≤z≤DA≤x≤B≤y≤C≤z≤D
问 能构成多少个三角形。
考虑只要满足 x + y > z 即可。
x + y 的范围 [ a+b, b+c ]
枚举 x + y ,判断符合情况的 z 的个数。对答案的贡献即为 cntz * x + y 的组合数。
x 在 [ a , b ] , y 在 [ i - b, i - a] ,y在 [ b , c ] 的范围,取小即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main(){ ll A,B,C,D; ll ans=0; scanf("%lld %lld %lld %lld",&A,&B,&C,&D); for(ll i=A+B;i<=B+C;i++){ if(i<=C)continue; ll cntz=min(i-C,D-C+1); ll l=max(i-B,B); ll r=min(C,i-A); ans+=(r-l+1)*cntz; } printf("%lld\n",ans); // cout<<ans<<endl; // csC // system("pause"); return 0; }
D - Phoenix and Science
设 每天晚上的 细胞数为 ai,那么sum a i = n
当天分裂多少,对答案的贡献增加多少。
考虑最快的即为倍增,贪心构造即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main(){ int t;cin>>t; while(t--){ ll n; // scanf("%lld",&n);n cin>>n;n--; vector<int>a;a.push_back(1); for(ll i=2;i<=n;i<<=1){ a.push_back(i); n-=i; } if(n)a.push_back(n); sort(a.begin(),a.end()); cout<<a.size()-1<<endl; for(int i=1;i<a.size();i++){ // printf("%d ",a[i]-a[i-1]); cout<<a[i]-a[i-1]<<" "; } cout<<endl; } // system("pause"); return 0; }
C - Hilbert's Hotel
对于所有的 k - > k + a [ k % n ]
n必然是一个周期。枚举 [ 0, n-1] 投射后的情况,判断有无重合即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+500; int a[N]; int main(){ int t;cin>>t; while(t--){ int n;cin>>n; map<int,int>mp; for(int i=0;i<n;i++){ cin>>a[i]; int x=(i+a[i])%n; x=(x+n)%n; mp[x]++; } bool flag=1; map<int,int>::iterator it; for(it=mp.begin();it!=mp.end();it++){ if(it->second>1){ flag=0; break; } } if(flag)puts("YES"); else puts("NO"); } // system("pause"); return 0; }
C - Yet Another Counting Problem
x%a %b != x %b %a 的 情况,从 0 开始,b为一个周期,每次撞到一个lcm(a ,b)即为一个周期的开始,注意区间的头和尾相交的部分
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+500; ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);} ll lcm(ll a,ll b){return a/gcd(a,b)*b;} int main(){ int t;cin>>t; while(t--){ ll l,r,a,b,Q;cin>>a>>b>>Q; if(a>b)swap(a,b); while(Q--){ cin>>l>>r; if(a==1||b==1){ cout<<0<<" "; continue; } ll tot=r-l+1,d=lcm(a,b); ll cnt=r/d-(l-1)/d; ll ld=(l-1)/d*d,rd=r/d*d; if(cnt>=2)tot-=(cnt-1)*b; if(cnt>=1)tot-=min(r-rd+1,b); if(ld+b-1>=l){ tot-=min(ld+ b-1-l+1,r-l+1); // cout<<"test"<<endl; } tot=max(0ll,tot); cout<<tot<<" "; } cout<<endl; } // system("pause"); return 0; }
C - Mixing Water
考虑交替 h 和 c 偶数必然为( h + c ) / 2 枚举奇数情况,当偶数次数越多,温度越小,二分出一个答案,保证 > = t ,判断 x-1 和 x 那个更接近。
#include<bits/stdc++.h> using namespace std; typedef long long ll; // #define long double double double h,c,t; double f(int x){ return ( ( x*(c+h) + h ))/ ( (2*x+1) ); } int main(){ int T;cin>>T; while(T--){ cin>>h>>c>>t; double d=(c+h)/2; if(h+c>=2*t){puts("2");continue;} if(t>=h){puts("1");continue;} int l=0,r=1e9,ans; while(l<=r){ int mid=(l+r)/2; if(f(mid)>=t)l=mid+1,ans=mid; else r=mid-1; } if( fabs(f(ans)-t )<=fabs( f(ans+1)-t) ){printf("%d\n",2*ans+1);} else printf("%d\n",2*ans+3); } // system("pause"); return 0; }
C - Nastya and Strange Generator
每次选取一个x,那么下一个必然是x+1,扫一遍即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+500; int pos[N],p[N]; int main(){ int T;cin>>T; while(T--){ int n;scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&p[i]); pos[ p[i] ]=i; } bool flag=1; for(int i=2;i<=n;i++){ if(pos[i]>pos[i-1]&&pos[i]!=pos[i-1]+1){flag=0;break;} } if(flag)puts("Yes"); else puts("No"); } // system("pause"); return 0; }
D - Nastya and Scoreboard
记录一个cnt [ i ] [ j ] 表示i个灯变成 j 的代价,那么贪心选择即可,但当前选择的数字可能会造成后面的无解,那么设 f [ i ] [ j ] 表示 第 i 位 花费 j 是否可行,
那么从右到左dp出可行解,再从左往右贪心取数字,保证后续过程有解。
#include <bits/stdc++.h> using namespace std; const int N=4e3+150; string s[N]; int f[N][N]={0},w[N][20]={0}; string M[10]={"1110111","0010010","1011101","1011011","0111010","1101011","1101111","1010010","1111111","1111011"}; int main(){ memset(f,0,sizeof f); memset(w,0,sizeof w); int n,k;cin>>n>>k; for(int i=1;i<=n;i++)cin>>s[i]; for(int i=1;i<=n;i++){ for(int j=0;j<=9;j++){ for(int l=0;l<7;l++){ if(s[i][l]=='0'&&M[j][l]=='1')w[i][j]++; else if(s[i][l]=='1'&&M[j][l]=='0'){w[i][j]=-1;break;} } } } f[n+1][0]=1; for(int i=n;i>=1;i--){ for(int j=0;j<=9;j++){ if(w[i][j]==-1)continue; for(int l=0;l<=k;l++){ if(f[i+1][l])f[i][ l + w[i][j] ]=1; } } } string ans=""; for(int i=1;i<=n;i++){ bool flag=0; for(int j=9;j>=0;j--){ if(w[i][j]>k||w[i][j]==-1)continue; if(f[ i+1 ][ k - w[i][j] ]==0)continue; else { flag=1; ans+=j+'0';k-=w[i][j]; break; } } if(!flag){ans="-1";break;} } cout<<ans<<endl; // system("pause"); return 0; }
C - Linova and Kingdom
考虑贪心做法,每次选取最外的点,这样贪心,每个点对答案的贡献为 dep [ i ] - size [ i ] ,贪心选择
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+500; #define pb push_back vector<int>e[N]; int n,k; ll size[N],dep[N]; void dfs(int u,int fa){ size[u]=1; for(int i=0;i<e[u].size();i++){ int v=e[u][i]; if(v==fa)continue; dep[v]=dep[u]+1; dfs(v,u); size[u]+=size[v]; } } int main(){ scanf("%d %d",&n,&k); for(int i=1,u,v;i<n;i++){ scanf("%d %d",&u,&v); e[u].pb(v);e[v].pb(u); } dep[1]=0; dfs(1,-1); // for(int i=1;i<=n;i++)cout<<size[i]-1<<" "; // cout<<endl; vector<ll>g; for(int i=1;i<=n;i++)g.push_back(dep[i]-size[i]+1); sort(g.begin(),g.end(),greater<ll>() ); // for(int i=0;i<n;i++)cout<<g[i]<<" "; // cout<<endl; ll ans=0; for(int i=0;i<k;i++)ans+=g[i]; cout<<ans<<endl; // system("pause"); return 0; }