作者很菜,看不懂补药( ̄(工) ̄)俺~~~~
学历史导致的
这个就直接暴力就行了,签到题~
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define sz(x) (int)x.size()
map<string ,int>ans;
string tian[]={ "jia", "yi", "bing","ding", "wu", "ji", "geng", "xin", "ren", "gui"};
string di[]={"zi","chou","yin","mao", "chen","si","wu", "wei", "shen", "you","xu", "hai"};
void solve(){
string c;
cin>>c;
cout<<ans[c]<<endl;
}
signed main()
{
int ii=0,jj=0;
for(int i=1984;i<=2043;i++){
string c=tian[ii]+di[jj];
ii=(ii+1)%10;
jj=(jj+1)%12;
ans[c]=i;
}
int t;cin>>t;
while(t--)
solve();
return 0;
}
学数数导致的
这个题目是要求找出数列 a1…an-1,问有多少个正整数数对 (p,q)使得数列中存在子序列 p,0,p,q
首先应该读懂题目,这是个正整数对,所以我们统计的时候0不能算在内,其次这是个序列
那么我们就可以先倒序遍历,统计当前位置后面出现的数字的种类个数
然后从头往后遍历,遇到0就更改idx的值,否则就进行另一个判断,看看是不是第一次出现,如果是的话,就给first_idx存储下标,这个的作用是为了防止0 1 1 这样的序列也计算进去了,其他的都挺简单啦
具体的看代码吧
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define sz(x) (int)x.size()
//模拟
//记录之前的值即可
void solve(){
int n;cin>>n;
vi f1(1e6+10,0);
vi cnt(1e6+10,0);
vi f2(1e6+10,0);
vi a(1e6+10,0);
vi count(1e6+10,0);
vi first_idx(1e6+10,1e6+10);
int c=0;
for(int i=0;i<n;i++)cin>>a[i];
for(int i=n-1;i>=0;i--){
if(f1[a[i]]==0&&a[i]!=0){
f1[a[i]]=1;
c++;
}
count[i]=c;
}
int idx=100001001;
int ans=0;
for(int i=0;i<n;i++){
if(a[i]!=0){
if(first_idx[a[i]]==1e6+10)first_idx[a[i]]=i;
if(cnt[a[i]]==0||f2[a[i]]==1){//没出现过或者已经算过了
cnt[a[i]]++;
}else if(first_idx[a[i]]<idx){//之前已经出现过了并且最先出现的小于idx
if(idx<i){//可以计算了
ans+=count[i+1];
f2[a[i]]=1;
}
}
}else{
idx=i;
}
}
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int t;cin>>t;
while(t--)
solve();
return 0;
}
学 DP 导致的
要求找最长子序列,直接打板子就好啦~
但是k需要处理一下,最长就是拼接26次
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define sz(x) (int)x.size()
//最长子序列模板啦
void solve(){
int q[100010]={0};
string a;cin>>a;;
string k;cin>>k;
int t = (k.length() >= 3) ? 26 : min(26, stoi(k));
string b=a;
for(int i=0;i<t-1;i++)a=a+b;
int n=sz(a);
q[0] = -2e9;
int len = 0;
for (int i = 0; i < n; i ++)
{
int l = 0, r = len;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (q[mid] < a[i]) l = mid;
else r = mid - 1;
}
len = max(len, r + 1);
q[r + 1] = a[i];
}
cout<<len<<endl;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;cin>>t;
while(t--)
solve();
return 0;
}
学几何导致的
这个是个规律,我们每次实现必须O(1)来实现
观察可以发现,如果k为奇数,那么一定没有结果啦
其次我们对于0来说,第一个垂直的是0+k/2 ,第二个是0+k/2+k,第三个是0+k/2+k+k.....
那么到这里应该就发现规律了,一直加到n即可,那么第二个就是从1开始了,第一个垂直的是1+k/2 ,第二个是1+k/2+k,第三个是1+k/2+k+k.....
在这我们先使k/=2;
我们可以得到0开始的垂直的数量为t=(n-1-k)/(k*2)+1;
那么是不是最后还会剩几个,是多少呢?其实余数就是mod=(n-1-k)%(k*2);
那么数量为t的计算完了,依次是t-1,t-2......1 ,而对于每个,他的数量都是2*k
直接求和相乘就行了ans=t*(mod+1)+((1+t-1)*(t-1)/2)*(2*k);
看代码吧
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vi vector<int>
#define sz(x) (int)x.size()
void solve(){
int n,k;cin>>n>>k;
if(k%2==1||(2*n<=k)){//差个条件判断
cout<<0<<'\n';
return;
}
k/=2;
int ans=0;
int t=(n-1-k)/(2*k)+1;
int mod=(n-1-k)%(2*k);
ans=t*(mod+1)+((1+t-1)*(t-1)/2)*(2*k);
cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int t;cin>>t;
while(t--)
solve();
return 0;
}