A. Number Transformation
又不仔细看题目!!A题都wa了三次……
#include<bits/stdc++.h>
using namespace std;
int gcd(int x,int y)
{
return y==0?x:gcd(y,x%y);
}
int main()
{
int i,j;
int t;cin>>t;
while(t--)
{
int x,y;cin>>x>>y;
double ex = (double)y/x;
int cc1 = ex;
if(cc1== ex){
cout<<1<<" "<<ex<<endl;
}else{
puts("0 0");
}
}
return 0;
}
B. Dictionary
#include<bits/stdc++.h>
using namespace std;
int v[30][30];
void in()
{
int idx = 1;
for(int i=0;i<26;i++){
for(int j = 0;j <26;j++){
if(i == j) continue;
v[i][j] = idx++;
}
}
}
int main()
{
int t;cin>>t;
in();
while(t--)
{
char a,b;cin>>a>>b;
cout<<v[a-'a'][b-'a']<<endl;
C. Infinite Replacement
这题看了大佬的解法后突然意识到写快速幂干嘛啊;
ll quick(int x,ll base)
{
ll ans = 1;
while(x)
{
if(x&1) ans=ans*base;
x>>=1;
base*=base;
}
return ans ;
}
//当base等于2的时候不就是位运算做嘛quick(x,2) = 1ll<<x;
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main()
{
int t;cin>>t;
while(t--)
{
string s;cin>>s;
string t;cin>>t;
int f = 0;//看t中是否有'a'
int f2 = 0;//看是否除了a还有别的字母
for(int i=0;i<t.length();i++){
if(t[i]=='a'){
f = 1;
}
if(t[i]!='a'){
f2 = 1;
}
}
if(f == 1 &&t.length()!=1){
puts("-1");
}else{//t中无'a'或者t的长度不为1
ll ans = 0;//主要2^50会爆int
if(f2 == 1 && f == 0){//有别的字母并且不含a
ans = 1ll<<s.length();
}else{//无别的字母
ans = 1;
}
cout<<ans<<'\n';
}
}
}
大佬的代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;scanf("%d",&t);
while(t--)
{
string s,t;
cin>>s>>t;
if(t == "a"){
puts("1");
}else{
bool f = 0;
for(int i = 0;i < t.length(); i++){
if(t[i] == 'a'){
f = 1;
break;
}
}
if(f){
puts("-1");
}else{
printf("%lld\n",1ll<<s.length());
}
}
}
return 0;
}
D. A-B-C Sort
如果A数组有奇数个元素,则 A 1 A_1 A1一定被放在B数组最中间然后又被拿出来放在C数组的第一个位置,所以如果A数组元素个数为奇数然后A中的第一个元素又不是A中最小的话,那边不能使C升序;当 A 1 A_1 A1是min时,那便不用考虑它,直接看剩下的偶数个元素,剩下偶数个元素中 A 2 A_2 A2和 A 3 A_3 A3最终一定被放在B的中间两个位置( A 1 A_1 A1左右两边),之后又被拿出来放到C中,当然 ,偶数个的时候A2和A3谁在A1左侧谁在右侧随意选择,之后拿出来放到C中的时候选了A1之后再从A3和A2选较小值便可。所以A2和A3至少有一个必定是剩下元素中的最小值,但是又要小于A1,于是我们便不断重复奇数长度、偶数长度的过程,在此过程中维护一个当前C的最大值就行。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int main()
{
int t;scanf("%d",&t);
while(t--)
{
int n;scanf("%d",&n);
vector<int>q;
for(int i = 1;i <= n; i++){
int x;scanf("%d",&x);
q.push_back(x);
}
int maxt = -1e9;
int f = 0;
while(!q.empty())
{
if(q.size()%2==1){
if(q[0] < maxt){
f = 1;
break;
}else{
maxt = q[0];
q.erase(q.begin());
}
}else{
int mint = min(q[0],q[1]);
if(mint < maxt){
f = 1;
break;
}else{
maxt = mint ;
auto it = q.begin();
if(q[0]>q[1]) it = q.begin()+1;
else it = q.begin();
q.erase(it);
}
}
}
puts(f?"NO":"YES");
}
return 0;
}
E. Breaking the Wall
花费代价1的价格对 A i A_i Ai攻击一次造成2点伤害,并且对 A i − 1 A_{i-1} Ai−1和 A i + 1 A_{i+1} Ai+1造成1点溅射伤害;
情况分析:
-
1:两个攻击点相隔2距离or more,则互相不产生溅射伤害,总攻击次数为 ⌈ A i 2 ⌉ \lceil{\frac{A_i}{2}\rceil} ⌈2Ai⌉+ ⌈ A j 2 ⌉ \lceil{\frac{A_j}{2}\rceil} ⌈2Aj⌉;
-
2:两个攻击点相距1距离,也不产生溅射伤害,但是如果 A i A_i Ai和 A j A_j Aj都为奇数的时候,我们可以对他们中间位置攻击一次,然后对i和j产生一点溅射伤害,让他两变成偶数,则总攻击次数为1+ ⌊ A i 2 ⌋ \lfloor{\frac{A_i}{2}\rfloor} ⌊2Ai⌋+ ⌊ A j 2 ⌋ \lfloor{\frac{A_j}{2}\rfloor} ⌊2Aj⌋;
-
3:两个攻击点相邻;每次让较大的数减少2,较少的减少1,每次总体减少3;
-
如果较大的数是较小的数的两倍即以上,则答案为 ⌈ m a x 2 ⌉ \lceil{\frac{max}{2}\rceil} ⌈2max⌉;
-
否则:若干步后两步数达到相同,然后在接下来的步骤里至多相差1,当前花费了 ⌊ A i + A j 2 ⌋ \lfloor{\frac{A_i+A_j}{2}\rfloor} ⌊2Ai+Aj⌋步数,达到(0,0)(0,1)或者(1,0)的状态,后两种则需要再攻击一次,则取上整, ⌈ A i + A j 2 ⌉ \lceil{\frac{A_i+A_j}{2}\rceil} ⌈2Ai+Aj⌉
-
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int a[N],b[N];
int n;
int main()
{
int i,j;
cin>>n;
for(i = 1;i <= n; i++){
scanf("%d",&a[i]);
b[i] = a[i];
}
sort(b+1,b+1+n);
int mint = ceil((double)b[1]/2) + ceil((double)b[2]/2);//情况1
for(i = 1;i <= n; i++){
if(i < n &&i > 1&& a[i-1]%2 == 1&&a[i+1]%2==1 ){//情况2那种特殊情况
int cc1 = 1+a[i-1]/2 + a[i+1]/2;
mint = min (mint,cc1);
}
if(i < n){//情况3
int max_ex = max(a[i],a[i+1]);
int min_ex = min(a[i],a[i+1]);
if(max_ex>=min_ex*2){
int cc2 = ceil((double)max_ex/2);
mint = min(mint,cc2);
}else{
int cc2 = ceil((double)(a[i]+a[i+1])/3);
mint = min(mint,cc2);
}
}
}
cout<<mint<<endl;
return 0;
}
F. Desktop Rearrangement
这句话"The next q lines describe queries. The i-th of them contains two integers xi and yi (1≤xi≤n;1≤yi≤m) — the position of the cell which changes its state (if this cell contained the icon before, then this icon is removed, otherwise an icon appears in this cell)."
的意思是:接下来的 q 行描述查询。其中的第 i 个包含两个整数 xi 和 yi (1≤xi≤n;1≤yi≤m) — 更改其状态的单元格的位置(如果此单元格之前包含图标,则此图标将被删除,否则此单元格中会出现一个图标)。也就是转换它。
然后输出修改后使得桌面“变好”的最小操作次数;
暴力做法:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
char s[N][N];
int col[N];//记录每一列"*"的数量
int n,m,q;
int main()
{
int i,j;
scanf("%d%d%d",&n,&m,&q);
for(i = 1;i <= n; i++) cin>>s[i]+1;
int sum = 0;//记录总的"*"得数量
for(i = 1;i <= n; i++){
for(j = 1;j <= m; j++){
//cout<<s[i][j];
if(s[i][j] == '*'){
sum++;
col[j]++;
}
}//cout<<'\n';
}
while(q--)
{
int x,y;scanf("%d%d",&x,&y);
if(s[x][y] == '*'){
sum--;
s[x][y] = '.';
col[y]--;//第y列
}else{
sum++;
s[x][y] = '*';
col[y]++;
}
int cc1 = sum/n;//都被填充满有几列
int cc2 = sum - cc1*n;//剩多少颗是不能填充满的
//然后我们去看前cc1列有多少棵"*",他们是本就不需要移动的
int cnt = 0;
for(i = 1;i <= cc1; i++){
cnt += col[i];
}
//然后再去看第cc1+1那列前cc2个中有多少个"*",他们也不需要额外移动
for(i = 1;i <= cc2; i++){
cnt+=(s[i][cc1+1]=='*');
}
cout<<sum-cnt<<endl;
}
return 0;
}
树状数组做法:
看了某乎上才知道可以转换成树状数组做法,昨天刚学树状数组,qwq;
首先将二维坐标转为一维坐标**(x,y) = ((y-1)*n+x)**;
然后每修改一个点就判断,如果是“*”,就add(pos,-1),pos表示当前点的一维x轴坐标,否则就add(pos,1);
修改完后统计前cnt个位置上有多少个“*”即可,然后与cnt作差;
#include<bits/stdc++.h>
using namespace std;
const int N = 1010,M = 1000010;
int tr[M];
char a[N][N];
int n,m,q;
int lowbit(int x)
{
return x&(-x);
}
void add(int u,int v)
{
for(int i = u;i <= n*m;i += lowbit(i)) tr[i]+=v;
}
int query(int x)
{
int res = 0;
for(int i = x;i >= 1;i -= lowbit(i)) res+=tr[i];
return res;
}
int get(int x,int y)
{
return (y-1)*n+x;
}
int main()
{
int i,j;
cin>>n>>m>>q;
int cnt = 0;//记录*的个数
for(i = 1;i <= n; i++){
cin>>a[i]+1;
}
for(i = 1;i <= n; i++){
for(j = 1;j <= m; j++){
if(a[i][j] == '*') add(get(i,j),1),cnt++;
}
}
while(q--)
{
int x,y;
cin>>x>>y;
int idx = get(x,y);
if(a[x][y] == '*'){
a[x][y] = '.';
add(idx,-1);
cnt--;
}else{
a[x][y] = '*';
add(idx,1);
cnt++;
}
cout<<cnt - query(cnt)<<'\n';
}
return 0;
}
G. Remove Directed Edges
还得多复习几次这个题;
要好好学英语啊!
给你n个顶点,m条边,顶点从1~n编号,保证没有多重边和自循环环;
i n v in_v inv表示顶点v的入度, o u t v out_v outv表示顶点v的出度;
要你从图中删除若干条边,然后v点新的入度为 i n v ′ {in_v}^ \prime inv′,新的出度为 o u t v ′ {out_v}^ \prime outv′,如果原本就是0就不需要删除;
需满足:
- i n v ′ {in_v}^ \prime inv′< i n v ′ {in_v}^ \prime inv′或者 i n v ′ {in_v}^ \prime inv′ = i n v {in_v} inv = 0;
- o u t v ′ {out_v}^ \prime outv′< o u t v ′ {out_v}^ \prime outv′或者 o u t v ′ {out_v}^ \prime outv′ = o u t v {out_v} outv = 0;
我们将一个点集S称之为cute定义为:对于S中的任意点v和u(v!=u),在未删除的路径上存在一条从u到v的路径;
请问:从图中删除一些边并且所有顶点的入度和出度都减小或保持等于 0 后,可爱集 S 的最大可能大小是多少?
分析:就是找出删除若干条边使得点集S“cute”后,从u到v的那条链的长度最长是多少?
用dp求最长链:由可达到的点来更新当前点的值;
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int h[N],e[N],ne[N],idx;
int n,m;
int in[N],out[N];//入度,出度
int dp[N];
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void dfs(int u)
{
if(dp[u]) return;//已经遍历过查找过最远能到达的地方
dp[u] = 1;
if(out[u] <= 1) return;//当前点已经不能再删了
for(int i = h[u];i != -1;i = ne[i]){
int j = e[i];
dfs(j);
if(in[j] >= 2) dp[u] = max(dp[u],dp[j]+1);
//若u点能到达j点,则也能到达j能到达的点,取大值;
}
}
int main()
{
int i,j;
cin>>n>>m;
memset(h,-1,sizeof(h));//初始化
for(i = 1;i <= m; i++){
int u,v;//u->v的一条边
cin>>u>>v;
add(u,v);
in[v]++,out[u]++;
}
for(i = 1;i <= n; i++) dfs(i);
int maxt = 0;
for(i = 1;i <= n; i++){
maxt = max(dp[i],maxt);
}
cout<<maxt<<endl;
return 0;
}