23ICPC济南站补题
济南…我想家了…
不拿牌没脸回家
原题链接
思路来源
D 思维
void solve() {int la,ra,lb,rb;cin>>la>>ra>>lb>>rb;int l=la+lb,r=ra+rb;if(r-l>=10)return cout<<9<<endl,void();//会经过有9的数//以下r-l<10int le=l%10,re=r%10;string ls=to_string(l),rs=to_string(r);int ans=0;if(re>=le){//前面的数都一样ans=re;for(auto c:rs)ans=max(ans,(int)(c-'0'));}else{ans=9;//会经过有9的数}cout<<ans<<endl;
}
A 找规律
多列几组样例找规律
不合法的情况:
int jud(char c){int res=-1;if(c=='('||c==')')res=0;if(c=='['||c==']')res=1;return res;
}
void solve() {string s;cin>>s;//总的成对的 连续的int cnt=0,con=0;forr(i,1,s.size()-1){if(jud(s[i])==jud(s[i-1])){cnt++;con++;if(con>=2)return cout<<"No"<<endl,void();}else con=0;}if(cnt==2||cnt==1)cout<<"Yes"<<endl;else cout<<"No"<<endl;
}
I 模拟 思维
只要找到尽量远的l,rl,rl,r让al>ara_l>a_ral>ar就换
注意排列后不一定归位,不能直接跳过排序的区间
如1 2 3 4 5 6 8 10 7 9->1 2 3 4 5 6 7 8 10 9
void solve() {int n;cin>>n;vector<int>a(n+1);int fg=0;forr(i,1,n){cin>>a[i];if(a[i]!=i)fg=1;}if(!fg)return cout<<0<<endl,void();vector<pii>ans;int st=1;forr(i,1,n)if(a[i]!=i){st=i;break;}for(int i=st;i<=n;i++){if(a[i]==i)continue;// int ed=n;for(int j=n;j>i;j--){if(a[i]>a[j]){// ed=j;ans.push_back({i,j});sort(a.begin()+i,a.begin()+j+1);//把大的放到后面 跟后面比较break;}}//不能直接跳过 前面被排序的中间也会有一些没归位的数// i=ed-1;//注意下一步i++}cout<<ans.size()<<endl;for(auto i:ans)cout<<i.first<<' '<<i.second<<endl;
}
G 并查集 图论
每行可翻转,每列维持1的数目<=1,有多少种行反转的组合
发现
- 有的行两两之间有一个固定的关系,有×2\times2×2的贡献,但是可能也有互相制约
- 有的行左右反转都可以,也有×2\times2×2的贡献
把关系转化成图
用并查集表示固定关系,相互连通的互斥,一个连通块就是一个关系,每个关系有×2\times2×2的贡献
1:如果第 i 列和第 n - i + 1 列的 1 的数量之和小于等于 1 那么这 个 1 所在的一行翻转和不翻转都可以
2:如果第 i 列和第 n - i + 1 列的 1 的数量之和为2
(1):如果这两个 1 在同一列,那么这两行(指的是这两个1所在的行)必须翻转一个
(2):如果这两个 1 不在同一列,那么这两行(同上)必须要不都翻转,要不都不翻转
const int N=1e6+5,C=1e6+10,mod=1e9+7,inf=1e9+10;
string s[N];
int r,c;
int fa[N*2];
void init(){forr(i,1,2*r)fa[i]=i;
}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void merge(int x,int y){int fx=find(x),fy=find(y);fa[fx]=fy;
}int tpow(int a,int b){int res=1;for(;b;b>>=1,(a*=a)%=mod)if(b&1)(res*=a)%=mod;return res;
}
void solve() {cin>>r>>c;init();forr(i,1,r){cin>>s[i];s[i]=' '+s[i];}forr(i,1,(c+1)/2){//奇数列要处理中间的,因为中心翻转后不变int cnt=0;forr(j,1,r){cnt+=(s[j][i]=='1')+(s[j][c-i+1]=='1');}if(cnt>2)return cout<<0<<endl,void();//对称两列上的1个数>2 怎么转每列也不能达到<=1}forr(j,1,(c+1)/2){vector<int>lp,rp;forr(i,1,r){if(s[i][j]=='1')lp.push_back(i);if(s[i][c-j+1]=='1')rp.push_back(i);}// cout<<lp.size()<<' '<<rp.size()<<endl;//i+r表示反转状态//在同一列:一行反转一行不变if(lp.size()==2){merge(lp[0],lp[1]+r);merge(lp[0]+r,lp[1]);}else if(rp.size()==2){merge(rp[0],rp[1]+r);merge(rp[0]+r,rp[1]);}//两列1的个数<2 翻不翻都可else if(lp.size()==0||rp.size()==0)continue;//不在同列:一起变else{merge(lp[0],rp[0]);merge(lp[0]+r,rp[0]+r);}}// forr(i,1,2*r)cout<<i<<' '<<fa[i]<<endl;forr(i,1,r){//第i行不变和反转在同一关系中 相互矛盾if(find(i)==find(i+r))return cout<<0<<endl,void();}int ans=0;forr(i,1,2*r)ans+=fa[i]==i;// cout<<ans<<endl;cout<<tpow(2,ans/2)<<endl;
}
K 滑动窗口 求中位数
思路来源
一开始想用二分去做,但是苦于check怎么求出长度内的修改次数。
ai+1−ai=1a_{i+1}-a_i=1ai+1−ai=1可以变成ai−i=aj−ja_i-i=a_j-jai−i=aj−j,每个数都减去下标后都改成相同的数会产生彩虹数组。
最小的操作次数就是改成长度内的中位数,使用双set维护中位数,有O(logn)O(logn)O(logn)的复杂度。
使用二分,check中定长窗口,set维护,总复杂度O(nlog2n)O(nlog^2n)O(nlog2n)会超时。
所以直接使用滑动数组,O(nlogn)O(nlogn)O(nlogn)找长度。
int n,k;
// set找中位数
multiset<int>gs,ls;//ls<mid<gs
int gsm,lsm;
// 平衡中位数两边
void blc(){while (ls.size()+1<gs.size()&&gs.size()){auto tp=gs.begin();gsm-=*tp,lsm+=*tp;ls.insert(*tp);gs.erase(tp);}while (ls.size()>gs.size()&&ls.size()){auto tp=ls.end();tp--;gsm+=*tp,lsm-=*tp;gs.insert(*tp);ls.erase(tp);}
}
void solve() {cin>>n>>k;vector<int>a(n+1);forr(i,1,n)cin>>a[i];if(n==1)return cout<<1<<endl,void();forr(i,1,n)a[i]=a[i]-i;//神奇性质:a[i+1]-a[i]=1=i+1-i a[i+1]-(i+1)=a[i]-i//滑动窗口gsm=lsm=0;gs.clear();ls.clear();int ans=1,mid=a[1],bg=1,sm=0;gs.insert(a[1]),gsm+=a[1];forr(i,2,n){if(a[i]>=mid)gs.insert(a[i]),gsm+=a[i];//滑进窗口else ls.insert(a[i]),lsm+=a[i];blc();mid=*gs.begin();sm=(ls.size()*mid-lsm)+(gsm-gs.size()*mid);while(sm>k){if(a[bg]>=mid)gs.erase(gs.find(a[bg])),gsm-=a[bg];else ls.erase(ls.find(a[bg])),lsm-=a[bg];blc();//增减数组后进行平衡 否则会内存错误remid=*gs.begin();sm=(ls.size()*mid-lsm)+(gsm-gs.size()*mid);bg++;//前面的滑出窗口}ans=max(ans,i-bg+1);}// cout<<"ans";cout<<ans<<endl;
}