根据难度排序。
个人总结向。
如果有什么讲的不清楚的欢迎留言私信交流~
题意: 给出两个时分秒表示的时间,问相差多少秒。
思路:
化成秒相减就可以了。
代码:
#include
#define pb push_back
#define ld long double
#define ll long long
#define ull unsigned long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define endl "\n"
#define pi acos(-1.0)
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=500+5;
const int mod=1e9+7;
const int mo=1e8;
ll h1,h2,m1,m2,s1,s2;
int main(){
string a,b;
cin>>a>>b;
ll cnt=0;
for(int i=0;i<a.size();i++){
if(a[i]==':') cnt++;
if(cnt==0)h1=h1*10+(a[i]-'0');
else if(cnt==1) m1=m1*10+(a[i]-'0');
else s1=s1*10+(a[i]-'0');
}
cnt=0;
for(int i=0;i<b.size();i++){
if(b[i]==':') cnt++;
if(cnt==0)h2=h2*10+(b[i]-'0');
else if(cnt==1) m2=m2*10+(b[i]-'0');
else s2=s2*10+(b[i]-'0');
}
s1=s1+m1*60+h1*3600;
s2=s2+m2*60+h2*3600;
cout<<abs(s1-s2)<<endl;
return 0;
}
题意: n∗m的矩阵中每一位为gcd(i,j),求所有k∗k的子矩阵中最大值的和。
思路:
用单调队列 / 滑动窗口 维护 二维的窗口最大值。
其实和一维单调队列差不多,先对原矩阵 a 每一行求出长度为 k 的(横向)窗口的最大值,得到 ma 矩阵。
然后第二次枚举的元素不再是原本的矩阵,而是 ma 矩阵。
对 ma 矩阵每一列求出长度为 k 的(竖向)窗口的最大值,覆盖在原 ma 矩阵的位置,这里选择覆盖是因为这题对内存要求较高。
特别注意的点是求 l c m ( i , j ) lcm(i,j) lcm(i,j),可以记忆化一下,否则有超时的可能。
还有切记数组开 int 就够了,统计最大值之和开 ll 即可。
代码:
#include
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=5001;
const int N=1e3+5;
const int mod=1e9+7;
int a[manx][manx],ma[manx][manx],q[manx];
ll ans,head,tail,n,m,k;
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!a[i][j])
a[i][j]=a[j][i]=i/__gcd(i,j)*j;
for(int i=1;i<=n;i++){
head=1,tail=0;
for(int j=1;j<=m;j++){
while(head<=tail&&q[head]<(j-k+1)) ++head;
while(head<=tail&&a[i][q[tail]]<=a[i][j]) --tail;
q[++tail]=j;
if(j>=k) ma[i][j]=a[i][q[head]];
}
}
for(int i=k;i<=m;i++){
head=1,tail=0;
for(int j=1;j<=n;j++){
while(head<=tail&&q[head]<(j-k+1)) ++head;
while(head<=tail&&ma[q[tail]][i]<=ma[j][i]) --tail;
q[++tail]=j;
if(i>=k) ma[j][i]=ma[q[head]][i];
}
}
for(int i=k;i<=n;i++)
for(int j=k;j<=m;j++)
ans+=ma[i][j];
printf("%lld",ans);
return 0;
}
题意: 选最少数量的链来覆盖整颗树,使树的每条边都至少被覆盖一次。
思路:
比赛的时候是标记路径,贪心的选择叶子,也就是乱搞
正解是 dfs 序, 然后一对一对的输出叶子,注意叶子数目为奇数时,需将剩下的叶子与根节点连接为一条路径。
代码:
#include
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
const int mod=1e9+7;
vector<ll>g[manx];
ll ans[manx];
ll n,m,k;
void dfs(ll u,ll pre){
if(g[u].size()==1) ans[++ans[0]]=u;
for(auto v:g[u]){
if(v==pre) continue;
dfs(v,u);
}
}
int main()
{
io;
cin>>n;
for(int i=1;i<n;i++){
ll u,v; cin>>u>>v;
g[u].pb(v); g[v].pb(u);
}
dfs(1,-1);
m=(ans[0]+1)/2;
cout<<m<<endl;
for(int i=1;i*2<=ans[0];i++)
cout<<ans[i]<<" "<<ans[i+m]<<endl;
if(ans[0]&1) cout<<ans[m]<<" "<<1<<endl;
return 0;
}
题意: 在平面上给若干个点,求一个过原点的圆,使得尽量多的点在圆上。
思路:
三点确定一个圆,由于过原点,所以只需要 O ( n 2 ) O(n^{2}) O(n2) 枚举即可,用 map 存半径进行统计。
精度的话 eps 设置 1 − 10 1^{-10} 1−10 比较好。
最后输出时不要忘了加上原点。
代码:
#include
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define eps 1e-10
using namespace std;
const int manx=1e6+5;
const int N=5e3+5;
const int mod=1e9+7;
struct Point{
double x,y;
Point(double x,double y){
this->x=x;
this->y=y;
}
Point(){
x=y=0;
}
};
Point p[N];
Point xin(Point a,Point b,Point c)
{
double a1 = b.x - a.x, b1 = b.y - a.y, c1 = (a1*a1 + b1*b1)/2;
double a2 = c.x - a.x, b2 = c.y - a.y, c2 = (a2*a2 + b2*b2)/2;
double d = a1*b2 - a2*b1;
return Point(a.x + (c1*b2 - c2*b1)/d, a.y + (a1*c2 -a2*c1)/d);
}
map<pair<double,double> ,ll>mps;
int main(){
ll n; scanf("%lld",&n);
ll k=0; Point zero(0,0);
for(int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
ll ans=0;
for(int i=1;i<=n;i++){
mps.clear();
for(int j=i+1;j<=n;j++){
if(fabs(p[i].x*p[j].y-p[i].y*p[j].x)>eps){
Point r=xin(p[i],p[j],zero);
ans=max(ans,++mps[mp(r.x,r.y)]);
}
}
}
printf("%lld\n",++ans);
return 0;
}
题意: 给一个长度为n的1−n的排列,开始为1,2,3,……,n,给出经过k次相同置换后得到的排列,问置换一次后的排列。
思路:
置换群的符合运算:
对于 A k = B A^{k} = B Ak=B ,两边乘一个 k 的逆元 inv,有 A k ∗ i n v = B i n v A^{k*inv} = B^{inv} Ak∗inv=Binv,化简得到: A = B i n v A = B^{inv} A=Binv
因为k为大质数,所以在置换时循环大小不会变化。
对于每一个环单独考虑,给定排列的环大小就是置换的循环大小,将 k . m o d . l e n k . mod .len k.mod.len 当做一次置换,那么只要找到 x ∗ k m o d l e n = 1 x∗k mod len=1 x∗k mod len=1 时的 x ,也就是 k 的逆元,做 x 次置换,就可以得到答案。
注意给出的是置换完的样子,而不是置换。
代码:
#include
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define eps 1e-10
using namespace std;
const int manx=1e6+5;
const int N=5e3+5;
const int mod=1e9+7;
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){ x=1,y=0; return a;}
ll gcd=exgcd(b,a%b,y,x);
y-=a/b*x;
return gcd;
}
ll inv(ll a,ll b,ll &x,ll &y){
ll gcd=exgcd(a,b,x,y);
if(gcd!=1) return -1;
else return (x+b)%b;
}
ll a[manx],vis[manx],ans[manx];
vector<ll>g;
int main(){
ll n,k; cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
if(vis[i]) continue;
vis[i]=1; g.clear(); g.pb(i);
ll x=a[i],y;
while(x!=i) g.pb(x),vis[x]=1,x=a[x];
if(g.size()==1){ans[i]=i; continue;}
x=inv(k%g.size(),g.size(),x,y);
for(int j=0;j<g.size();j++) ans[g[j]]=g[(j+x)%g.size()];
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
return 0;
}
题意: 给出若干个字符串,定义 f ( s i , s j ) f(si,sj) f(si,sj) 为串 S i Si Si 的前缀与串 S j Sj Sj 的后缀的最大重合长度。
求 ∑ ∑ f ( s i , s j ) 2 ∑∑f(si,sj)^{2} ∑∑f(si,sj)2 。
思路:
对每个字符串的后缀进行hash,用map计数。
然后枚举每个字符串,枚举的时候计算前缀hash值,map中相同hash值的个数就是与其相匹配的后缀个数。
特别注意的是,题目要求的是最大的重合长度,那么如何取消短的前缀造成的重复计算?
这个时候可以想到 n e x t next next 数组, n e x t [ j ] next[j] next[j] 即可回滚长度,也就是说明有 子串 ( 1 − j ) (1-j) (1−j) 与 子串 ( 1 − n e x t [ j ] ) (1-next[j]) (1−next[j]) 有相同的前缀,这个时候就用 a n s [ n e x t [ j ] ] − = a n s [ j ] ans[next[j]]-=ans[j] ans[next[j]]−=ans[j],用可回滚处的答案减去较长子串对答案的贡献。
最后还有一个坑点就是 n e x t next next 数组是对于字符串来说整体向右移动了一位,所以部分地方枚举或者计算答案需要注意下标。
代码:
#include
#define mp make_pair
#define pb push_back
#define ll long long
#define ull unsigned long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define eps 1e-10
using namespace std;
const int manx=1e6+5;
const int N=5e3+5;
//const int mod=1e9+7;
const int mod=998244353;
const ull mo=233;
string s[manx];
ll nexts[manx],ans[manx];
map<ull,ll>cnt;
void dohash(string s){
ll n=s.size(); ull sum=0,base=1;
for(int i=n-1;i>=0;i--){
sum+=(s[i]-'a'+1)*base;
base*=mo; cnt[sum]++;
}
}
void donext(string p){
ll i=0,j=-1,m=p.size();
nexts[i]=j;
while(i<m){
if(j==-1||p[i]==p[j]) nexts[++i]=++j;
else j=nexts[j];
}
}
int main(){
ll n; cin>>n; ll x=0;
for(int i=1;i<=n;i++) cin>>s[i],dohash(s[i]);
for(int i=1;i<=n;i++){
donext(s[i]);
ull sum=0;
for(int j=0;j<s[i].size();j++){
sum=sum*mo+(s[i][j]-'a'+1);
ans[j+1]=cnt[sum];
}
for(int j=1;j<=s[i].size();j++)
if(nexts[j]>0) ans[nexts[j]]-=ans[j];
for(int j=1;j<=s[i].size();j++)
x+=ans[j]%mod*j%mod*j%mod,x%=mod;
}
cout<<x<<endl;
return 0;
}