1002
给出干支纪年法,确定是哪一年?只要求一个周期里的年份,也就是60年内,直接打表先确定每个干支纪年法对应的年份,然后查表
#include<bits/stdc++.h>
using namespace std;
vector<string>va={"jia", "yi", "bing", "ding", "wu", "ji", "geng", "xin", "ren", "gui"};
vector<string>vb={"zi", "chou", "yin", "mao", "chen", "si", "wu", "wei", "shen", "you", "xu", "hai"};
map<string,int>mp;
void solve(){
string s;
cin>>s;
cout<<mp[s]<<'\n';
}
int main(){
int t;
cin>>t;
int x=0,y=0;
for(int i=1984;i<=2043;i++){
string s=va[x]+vb[y];
mp[s]=i;
x++;
y++;
x%=10;
y%=12;
}
while(t--){
solve();
}
}
1003
计数,问子序列 ( p , 0 , p , q ) (p,0,p,q) (p,0,p,q)出现多少次?
q q q只出现一次,可以枚举 q q q,那么对于每个 q q q,对答案的贡献就是他左侧 ( p , 0 , p ) (p,0,p) (p,0,p)的个数。 ( p , 0 , p ) (p,0,p) (p,0,p)可以先预处理,具体来说检查一个 p p p能不能构成 ( p , 0 , p ) (p,0,p) (p,0,p)可以检查它左侧是否存在另一个 p p p,并且中间有至少一个 0 0 0,那贪心的想,上一个 p p p肯定要取最左侧的,这样中间的范围尽可能大,出现 0 0 0的可能更大,因此这需要我们记录每个元素第一次出现的位置。
另外需要注意,要求的是满足条件的不同的 ( p , q ) (p,q) (p,q)对数,因此一个 q q q只能被计算一次,那么贪心的想,应该取尽可能靠右的 q q q,左侧出现 ( p , 0 , p ) (p,0,p) (p,0,p)的可能更大。
void solve(){
int n;
cin>>n;
vector<int>a(n+1),sum(n+1);
int mx=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+(a[i]==0);
mx=max(mx,a[i]);
}
vector<int>fi(mx+1),lst(mx+1);
for(int i=1;i<=n;i++){
if(fi[a[i]]==0){
fi[a[i]]=i;
}
lst[a[i]]=i;
}
vector<int>vis(mx+1);
int tot=0;
int ans=0;
for(int i=1;i<=n;i++){
if(i==lst[a[i]]&&a[i]){
ans+=tot;
}
if(fi[a[i]]&&a[i]){
if(sum[i-1]-sum[fi[a[i]]]>0){
if(!vis[a[i]]){
vis[a[i]]=1;
tot++;
}
}
}
}
cout<<ans<<'\n';
}
1004
思维+dp
一个只含小写字母的字符串,拼接最多 k = 1 0 100 k=10^{100} k=10100次,问 L I S LIS LIS的长度?
注意到字符集很小,也就是元素种类数很小,并且是严格递增的 L I S LIS LIS,因此最大长度显然只有 26 26 26,构造方式是第一个周期取 a a a,第二个周期取 b b b,以此类推。因此拼接次数很大,并不需要真的去 d p dp dp,可以直接输出答案。
具体来说,字符串里不同元素个数是 n n n的话, k > = n k>=n k>=n时答案就是 n n n, k < n k<n k<n时可以直接 d p dp dp,由于 k k k不大复杂度不会太高
需要注意的是 k k k很大,直接读取会爆,可以用字符串读取然后判断长度。
void solve(){
int n,k;
string s,ss;
cin>>s>>ss;
n=s.size();
if(ss.size()>10){
k=1000;
}
else{
k=stoi(ss);
}
set<char>st;
for(int i=1;i<=n;i++){
st.insert(s[i-1]);
}
if(k>=st.size()){
cout<<st.size()<<'\n';
}
else{
string t;
for(int i=0;i<k;i++){
t+=s;
}
vector<int>mx(26);
int ans=0;
for(int i=1;i<=n*k;i++){
int pre=0;
int x=t[i-1]-'a';
for(int j=0;j<x;j++){
pre=max(pre,mx[j]);
}
mx[x]=max(mx[x],pre+1);
ans=max(ans,mx[x]);
}
cout<<ans<<'\n';
}
}
1005
伪装成计算几何的数学(很多计算几何都是别的知识点伪装的)
给一个直线,绕固定点转 n = 1 e 9 n=1e9 n=1e9次,每次转 180 / k 180/k 180/k度,问有多少对直线垂直?
首先显然的是,由于垂直是把 180 180 180度对半分了,只有 k k k是偶数才能对半分,奇数直接无解。
其次,可能出现循环,转一圈就转回来了,因此最终,每一个度数的直线条数,都是类似于整除,带几个余数的样子的,这是个经典模式,也就是首先 y y y个元素都是 t = x / y t=x/y t=x/y,然后余数是 r r r的话,前 r r r个可以加一,变成 x / y + 1 = t + 1 x/y+1=t+1 x/y+1=t+1.
此时我们知道每个角度的元素个数了,然后就是个计数问题,一个度数 a a a,只能和 a − 90 a-90 a−90配对,那么对答案的贡献就是 c n t [ a ] ∗ c n t [ a − 90 ] cnt[a]*cnt[a-90] cnt[a]∗cnt[a−90]。由于 c n t cnt cnt的值实际上最多两种 ( t , t + 1 ) (t,t+1) (t,t+1),我们只要知道这两种值的个数就可以可以 O ( 1 ) O(1) O(1)计算了
这里分类讨论一下,如果余数 r > n / 2 r>n/2 r>n/2,那么有一部分是 ( t + 1 ) ∗ ( t + 1 ) (t+1)*(t+1) (t+1)∗(t+1),剩下是 ( t + 1 ) ∗ t (t+1)*t (t+1)∗t。如果 r < = n / 2 r<=n/2 r<=n/2,那么一部分是 ( t + 1 ) ∗ t (t+1)*t (t+1)∗t,一部分是 t ∗ t t*t t∗t
void solve(){
int n,k;
cin>>n>>k;
int ans=0,ans1=0;
if(k%2){
ans=0;
}
else{
int r=n%k;
int x=n/k;
if(r>=k/2){
ans+=(r-k/2)*(x+1)*(x+1);
ans+=(k-r)*(x+1)*x;
}
else{
ans+=r*(x+1)*x;
ans+=(k/2-r)*x*x;
}
}
cout<<ans<<'\n';
}
1006
博弈sg打表/结论
有一坨转移,如图
有直接分析的方法,可以把三种物品都变成第一种,也就是有
a
+
2
b
+
4
c
a+2b+4c
a+2b+4c个第一种物品,然后每次可以拿
0
−
3
0-3
0−3个,这是经典
n
i
m
nim
nim游戏。模
4
4
4余
0
0
0的话,后手可以控制自己拿的个数,让每一轮自己拿的+对手拿的=4,这样最后可以让先手没得拿,也就是先手必败。否则,先手可以拿几个,变成模
4
4
4余
0
0
0留给后手,后手必败,先手必胜。
但是看不出转移,也可以打表。一种方式是直接三重循环枚举所有 a b c abc abc的组合,输出是必败还是必胜,但这样没那么好观察。关键在于这里是三个变量的,三重循环打表,同一时间只有一个变量在变化,不好看出三种变量的总的关系。对于这种多个变量的博弈,一种比较好观察的打表是,把三个变量压缩成一个整数,给每个变量安排一个 b a s e p base^p basep,由于 b a s e i base^i basei之间是互相独立的,不同的 a b c abc abc压缩后可以对应不同的值。
本题就可以 z = a + 2 b + 4 c z=a+2b+4c z=a+2b+4c的方式压缩,这样压缩完就能看出来模 4 4 4余 0 0 0是必败,其余都是必胜。这个压缩方式和第一种思路的结论是一样的。
这题的两个教训是:
多个变量的打表可以压缩,就算不压缩,也应该多重循环打表,否则可能有遗漏;
打表的时候可以猜想,然后验证,但是猜想只是猜想,不要认定一个猜想就是对的,如果发现不对,应该迅速换一个猜想继续验证,不要死磕。并且选择猜想的时候,应该是基于基础打表(比如多重循环)来观察的,观察到某些规律,再来验证,而不是随便想一个猜想就开始验证。
这题赛时完全可以观察出来的,但是我随便选择了一个猜想,然后就掉进去了,后面一直在思考这个猜想,并没有及时更换思路。并且我这个猜想也是随便想的,并没有三重循环打表,然后观察,实际上错得离谱,是无意义的猜想。
void solve(){
int a,b,c;
cin>>a>>b>>c;
int x=a+2*b+4*c;
if(x%4){
cout<<"Alice\n";
}
else{
cout<<"Bob\n";
}
}