CF #506 DIV3
A:
题意:求匹配的最长前缀和后缀
思路:kmp求next数组或直接枚举前缀结束的位置即可。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MX = 1e6+5;
int main(){
int n,k;cin>>n>>k;
string t;cin>>t;int lt=t.length();
int u=-1;
for(int i=0;i<lt-1;i++){
if(t[i]==t[lt-1]){
int p1=i,p2=lt-1,tag=1;
for(int j=p1;j>=0;j--){
if(t[j]!=t[p2--]) {tag=0;break;}
}
if(tag) u=i;
}
}
string pre=t.substr(0,u+1);
string suf=t.substr(u+1,lt);
cout<<pre;
while(k--)cout<<suf;
return 0;
}
B
题意:给递增数组a,求最长的子序列,满足每项小于等于前一项的两倍。
思路:显然最长的子序列一定是数组a的子串(也就是连续),那么从头到尾扫一遍即可。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MX = 1e6+5;
int a[MX];
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int ans=1,t=1;
for(int i=2;i<=n;i++){
if(a[i]>a[i-1]*2) ans=max(ans,t),t=1;
else t++;
}
cout<<max(ans,t)<<endl;
return 0;
}
C
题意:给n个区间,求拿走一个区间之后,所有区间的交最大的区间长度。
思路1:影响答案最大的显然是L最大的和R最小的,那么比较删这两种哪种最后得到的区间长度大。
思路2:
思路1,我的代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
const int MX = 1e6+5;
int n;
pair<int,int>p[MX];
int del(int u){
int l=INT_MIN,r=INT_MAX;
for(int i=1;i<=n;i++){
if(i==u)continue;
l=max(l,p[i].FI);
r=min(r,p[i].SE);
}
return max(0,r-l);
}
int main(){
cin>>n;
int mi=INT_MAX,mx=INT_MIN,ui,ux;
for(int i=1;i<=n;i++){
scanf("%d%d",&p[i].FI,&p[i].SE);
if(p[i].FI>mx) {
mx=p[i].FI,ux=i;
}
if(p[i].SE<mi){
mi=p[i].SE,ui=i;
}
}
cout<<max(del(ux),del(ui));
return 0;
}
D
题意:给n个数字,求能挑出几个不一样的二元对,使得二元对拼接起来后得到的数字x,使x%k==0
思路:枚举第一个数字ai,那么第二个数字可以枚举它的位数wi(最多10位),设第二个数字为aj,那么可得结论aj%k==(k-ai*10^wi)%k,
然后用map离散化预处理每个数%k即可。(1e9是10位数啊!!! )记得统计答案的时候,小心map自动构造无用数据...
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
const int MX = 1e6+5;
int a[MX],le[MX];
int n,k;
map<int,int>mp[11];
int getwei(LL x){
int wei=0;
while(x){wei++,x/=10;}
return wei;
}
int main(){
//cout<<getwei(LLONG_MAX)<<' '<<getwei(INT_MAX)<<' '<<getwei(1e9);
LL ten_pow[20];
ten_pow[0]=1;
cin>>n>>k;
for(int i=1;i<=11;i++) ten_pow[i]=ten_pow[i-1]*10LL%k;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);le[i]=getwei(a[i]);
mp[le[i]][a[i]%k]++;
}
LL ans=0;
for(int i=1;i<=n;i++){
mp[le[i]][a[i]%k]--;
for(int j=1;j<=10;j++){
LL now=k-(LL)a[i]*(ten_pow[j])%k;
auto it=mp[j].find((now==k?0:now));
if(it!=mp[j].end())ans+=(LL)it->second;
}
mp[le[i]][a[i]%k]++;
}
cout<<ans;
return 0;
}
E
题意:给一个树,边权都是1,求至少添加几条边,能使得顶点1到别的点的最短路长度小于等于2
https://codeforc.es/blog/entry/61439(官方题解
(我的复杂树形dp)思路:定义dp[][2][2] ,1号点是根节点,显然都应该和根节点连边是可行的
dp[i][0][0]:i点和i点的父亲节点都不和根节点连边状态下,i点这颗子树(包括i)需要的最小代价。
dp[i][1][1]:i点和i点的父亲节点都和根节点连边状态下,i点这颗子树(包括i)需要的最小代价。
dp[i][0][1]:i点和根节点连边&&i点的父亲节点不和根节点连边状态下,i点这颗子树(包括i)需要的最小代价。
dp[i][1][0]:i点不和根节点连边&&i点的父亲节点和根节点连边状态下,i点这颗子树(包括i)需要的最小代价。
注意叶子节点,和第三层的叶子节点。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
const int MX = 2e5+5;
vector<int>g[MX];
int de[MX],fa[MX];
int dp[MX][2][2];
void getde(int now,int pre,int d){//得到深度和父亲节点
de[now]=d;fa[now]=pre;
for(auto it:g[now]){
if(it==pre) continue;
getde(it,now,d+1);
}
}
int dfs(int now,int pre,int cp,int cn){ //当前节点,父亲节点,父亲节点连边否,当前节点连边否
if(dp[now][cp][cn]!=-1)return dp[now][cp][cn]; //记忆化搜索
if(cn){ //如果当前节点连边,儿子节点可以连或不连
int cnt=0;
for(int it:g[now]){
if(it==pre)continue;
cnt=cnt+min(dfs(it,now,1,1)+1,dfs(it,now,1,0));
}
return dp[now][cp][cn]=cnt;
}
if(cp){ //如果当前节点不连边,父亲节点连边,儿子节点可以连或不连(当前儿子节点有节点)
int cnt=0;
for(int it:g[now]){
if(it==pre)continue;
if(g[it].size()>1)
cnt=cnt+min(dfs(it,now,0,1)+1,dfs(it,now,0,0));
else
cnt=cnt+dfs(it,now,0,1)+1;
}
return dp[now][cp][cn]=cnt;
}
int tag=0; //如果都不连边,儿子节点必须要有一个连边
for(int it:g[now]){
if(it==pre)continue;
if(g[it].size()==1) {tag=1;break;}
}
if(tag==1||de[now]==3){
int cnt=0;
for(int it:g[now]){
if(it==pre)continue;
if(g[it].size()==1) cnt+=dfs(it,now,0,1)+1;
else cnt=cnt+min(dfs(it,now,0,1)+1,dfs(it,now,0,0));
}
return dp[now][cp][cn]=cnt;
}
else {
int cnt=0;int mi=INT_MAX;
for(int it:g[now]){
if(it==pre)continue;
cnt=cnt+min(dfs(it,now,0,1)+1,dfs(it,now,0,0));
mi=min(mi,dfs(it,now,0,1)+1-dfs(it,now,0,0));
}
if(mi>0) return dp[now][cp][cn]=cnt+mi;
return dp[now][cp][cn]=cnt;
}
}
int main(){
int n;cin>>n;
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
g[u].push_back(v),g[v].push_back(u);
}
memset(dp,-1,sizeof(dp));
getde(1,0,1);
int ans=0;
for(int i=1;i<=n;i++){
if(de[i]==3){
ans=ans+min(dfs(i,fa[i],0,0),dfs(i,fa[i],0,1)+1);
}
}
cout<<ans;
return 0;
}
F
题意:有a个blue正方块,b个red正方块,求a+b个方块拼成一个矩形s,且矩形内有一块颜色一样的矩形(面积为a或者b)。
思路:先预处理出来,a的因子,b的因子,c的因子。
枚举大矩形s的长度(chang),然后可以算出s的宽(kuan),那么判断可以满足a或者b是否可以是小矩形,假设a是组成的小矩形,那么a的宽度大于(Sa/kuan),那么二分找到这个宽度,判断是否合理即可。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
const int MX = 1e6+5;
vector<LL>va,vs,vb;
int main(){
LL a,b;cin>>a>>b;
LL sum=a+b;
for(LL i=1;i*i<=sum;i++)if(sum%i==0)vs.push_back(i),vs.push_back(sum/i);
for(LL i=1;i*i<=a;i++)if(a%i==0)va.push_back(i),va.push_back(a/i);
for(LL i=1;i*i<=b;i++)if(b%i==0)vb.push_back(i),vb.push_back(b/i);
sort(vs.begin(),vs.end());vs.erase(unique(vs.begin(),vs.end()),vs.end());
sort(va.begin(),va.end());va.erase(unique(va.begin(),va.end()),va.end());
sort(vb.begin(),vb.end());vb.erase(unique(vb.begin(),vb.end()),vb.end());
LL ans=LLONG_MAX;
for(LL it:vs){
LL gao=sum/it;
LL chang=a/gao+(a%gao!=0);
auto id1=lower_bound(va.begin(),va.end(),chang);
if(id1!=va.end()&&(*id1)<=it) {ans=min(ans,gao*2LL+it*2LL);continue;}
chang=b/gao+(b%gao!=0);
auto id2=lower_bound(vb.begin(),vb.end(),chang);
if(id2!=vb.end()&&(*id2)<=it) {ans=min(ans,gao*2LL+it*2LL);}
}
cout<<ans;
return 0;
}