295B - Greg and Graph floyd
由于对Floyd的本质还是不大清楚所以做这个题还是费了点功夫,可以把整个过程反过来,就变成了解锁每个点对当前已有点的影响,其实这样也就相当于一个floyd了,值得注意的是虽然有的点没有解锁,但是是可以参与转移的,只不过累加和的时候不能加上他,为什么可以参加转移呢?因为最开始的那个k循环实际的意义就是将k这个点作为中转点来更新i和j的最短路,k是被解锁的,所以如果i和j通过k转移也是合法的,这样还不会漏掉情况
题解 P1119 【灾后重建】 - Time_Rune 的博客 - 洛谷博客 (luogu.com.cn)
#include <bits/stdc++.h>
using namespace std;
#define int long long
//const int mod=1e9+7;
const int inf=1e18;
const int N = 2e6+100;
int n,g[505][505],a[505][505],ans[505],b[505],vis[505];
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//cout<<(1LL<<19)<<endl;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
g[i][j]=a[i][j];
}
for(int i=1;i<=n;i++) cin>>b[i],vis[i]=0;
for(int k=n;k>=1;k--)
{
vis[b[k]]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
g[i][j]=min(g[i][j],g[i][b[k]]+g[b[k]][j]);
// if(k==2&&i==3&&j==4) cout<<g[i][j]<<" jjj "<<i<<" "<<j<<" "<<g[i][b[k]]<<" "<<g[b[k]][j]<<" "<<b[k]<<endl;
}
int res=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
//if(k==2) cout<<g[i][j]<<" "<<i<<" "<<j<<endl;
if(vis[i]&&vis[j]) res+=g[i][j];
}
ans[k]=res;
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
system("pause");
return 0;
}
/*
4
0 57148 51001 13357
71125 0 98369 67226
49388 90852 0 66291
39573 38165 97007 0
2 3 1 4
*/
D - Range and Partition 构造,双指针
最关键的是找到合适的x,y,先假设已经找到了,考虑如何输出,设in为在值域里面的数的个数,out为不在的个数,那么只要in==out+1了就可以输出这个区间,最后一段直接输出就可以,一定是满足条件的,然后可以发现前k-1段都是in==out+1,后面那一段in>=out+1,所以总的就是in>=out+k,所以就可以按照这个条件去枚举了,可以双指针也可以二分
Codeforces Round #768 (Div. 2) D(二分答案/双指针) - 知乎 (zhihu.com)
#include <bits/stdc++.h>
using namespace std;
#define int long long
//const int mod=1e9+7;
const int inf=1e18;
const int N = 2e6+100;
int t,n,k,a[N],sum[N],tx[N];
bool check(int l,int r)
{
return (2LL*(sum[r]-sum[l-1])-n>=k);
}
void print(int x,int y)
{
int in=0,out=0,l=1,cnt=1;
for(int i=1;i<=n&&cnt<k;i++)
{
if(a[i]>=x&&a[i]<=y) in++;
else out++;
if(in>out)
{
cout<<l<<" "<<i<<endl;
l=i+1;cnt++;in=0,out=0;
}
}
cout<<l<<" "<<n<<endl;
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//cout<<(1LL<<19)<<endl;
cin>>t;
while(t--)
{
cin>>n>>k;
for(int i=0;i<=n;i++) sum[i]=tx[i]=0;
for(int i=1;i<=n;i++) cin>>a[i],tx[a[i]]++;
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+tx[i];
int ansl=1,ansr=n;
for(int i=1,l=1;i<=n;i++)
{
while(l<=i&&check(l,i))
{
if(ansr-ansl>i-l)
{
ansr=i,ansl=l;
}
l++;
}
}
cout<<ansl<<" "<<ansr<<endl;
print(ansl,ansr);
}
system("pause");
return 0;
}
P5656 【模板】二元一次不定方程 (exgcd)
求x,y的最小正整数解,一开始看了一个没有代码的题解越算越不对直接emo了
【题解】【P5656-【模板】二元一次不定方程(exgcd)】 - 古明地觉世界第一! - 洛谷博客
#include <bits/stdc++.h>
using namespace std;
#define int long long
//const int mod=1e9+7;
const int inf=1e18;
const int N = 2e6+100;
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
int r=exgcd(b,a%b,x,y);
int temp=y;
y=x-(a/b)*y;
x=temp;
return r;
}
signed main()
{
//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//cout<<(1LL<<19)<<endl;
int t,a,b,c;
cin>>t;
while(t--)
{
cin>>a>>b>>c;
int g=__gcd(a,b);
if(c%g)
{
cout<<"-1\n";
continue;
}
int x,y,p=b/g,q=a/g,k;
exgcd(a,b,x,y);
x*=c/g;y*=c/g;
if(x<0) k=ceil((1.0-x)/p),x+=p*k,y-=q*k;
else if(x>=0) k=(x-1)/p,x-=p*k,y+=q*k;
//上面的操作都是将x变为最小正整数
if(y>0)
{
int num=(y-1)/q+1;//就是看看y可以减多少次q,但是一开始的y也是一个解所以要加1
int minx=x;
int miny=(y-1)%q+1;//这一步我的理解是y可能是q的倍数,为了取余q不等于0所以只要减到y==q就可以了
int maxx=x+(y-1)/q*p;//y可以减多少次q,x就可以加多少次p
int maxy=y;
cout<<num<<" "<<minx<<" "<<miny<<" "<<maxx<<" "<<maxy<<endl;
}
else
{
int minx=x;
int miny=y+q*ceil((1.0-y)/q);
cout<<minx<<" "<<miny<<endl;
}
}
system("pause");
return 0;
}
E - Draw a triangle 向量求三角形面积,exgcd
设第三个点的坐标为x3,y3,则可以求出两条向量A(x2-x1,y2-y1),B(x3-x1,y3-y1),然后两个向量叉乘的模的一半就是三角形的面积S=-(y2-y1)*(x3-x1)+(x2-x1)*(y3-y1),设x=x3-x1,y=y3-y1,a=-(y2-y1),b=(x2-x1),那么式子就变成了S=ax+by,然后就是求x,y让S最小,结论是S为gcd(a,b)时是最小的,所以直接扩展欧几里得算就可以,最后exgcd好像不能算负数的系数,所以就先转化成正的,然后求得的x和y再取个反就行
E. Draw a triangle(计算几何+EXGCD)_追随远方的某R的博客-CSDN博客
#include <bits/stdc++.h>
using namespace std;
#define int long long
//const int mod=1e9+7;
const int inf=1e18;
const int N = 2e6+100;
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
int r=exgcd(b,a%b,x,y);
int temp=y;
y=x-(a/b)*y;
x=temp;
return r;
}
signed main()
{
//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//cout<<(1LL<<19)<<endl;
int t;
cin>>t;
while(t--)
{
int x1,y1,x2,y2,x3,y3;
cin>>x1>>y1>>x2>>y2;
int a=-(y2-y1),b=x2-x1;
int f1=1,f2=1;
if(a<=0)
{
f1=-1;
a*=f1;
}
if(b<=0)
{
f2=-1;
b*=f2;
}
exgcd(a,b,x3,y3);
x3*=f1;y3*=f2;
x3+=x1;
y3+=y1;
cout<<x3<<" "<<y3<<endl;
}
system("pause");
return 0;
}
D-史莱姆的战术训练_并查集
先计算出每一种的粘度然后从大到小排序,可以发现只要从大到小遍历就只要管这个地方访没访问到就可以了,因为后来的一定比不上之前的粘度大,一个一个找的话会很慢,这时候又可以用并查集来辅助跳跃了,这个只要管行之间的跳跃就可以,不然写代码有点太麻烦了,,,
为了更加的优化,列最好是要比行多的,所以如果n>m就交换一下
齐鲁工业大学第四届程序设计竞赛题解_牛客博客 (nowcoder.net)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
//const int mod=1e9+7;
const int inf=1e18;
const int N = 1e7+100;
int n,m,p,c,a,b,s[N],col[N],vis[N],ori[N],ans[N];
int findd(int x){return x==s[x]?x:s[x]=findd(s[x]);}
struct node
{
int x1,y1,x2,y2,co;
bool operator<(const node &a)const
{
return co>a.co;
}
}q[N];
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m>>p>>c>>a>>b;
int flag=0;
if(n>m) swap(n,m),flag=1;
for(int i=1;i<=c;i++)
{
cin>>col[i];
ori[col[i]]=i;
//cout<<ori[col[i]]<<" "<<i<<endl;
col[i]=a*col[i]+b;
}
for(int i=1;i<=p;i++)
{
if(flag) cin>>q[i].y1>>q[i].x1>>q[i].y2>>q[i].x2>>q[i].co;
else cin>>q[i].x1>>q[i].y1>>q[i].x2>>q[i].y2>>q[i].co;
q[i].co=col[q[i].co];
}
sort(q+1,q+p+1);
for(int i=1;i<=n*m+5;i++) s[i]=i;
for(int i=1;i<=p;i++)
{
int x1=q[i].x1,x2=q[i].x2,y1=q[i].y1,y2=q[i].y2,co=q[i].co;
if(co<=0) continue;
for(int j=x1;j<=x2;j++)
{
for(int k=findd((j-1)*m+y1);k<=((j-1)*m+y2);k=findd(k+1))
{
ans[k]=ori[(co-b)/a];
//cout<<co<<" "<<b<<" "<<a<<" "<<(co-b)/a<<" "<<ori[(co-b)/a]<<" "<<ori[4]<<endl;
s[k]=k+1;
}
}
}
if(flag)
{
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++) cout<<ans[(j-1)*m+i]<<" ";//这个地方值得注意一下,交换nm后该如何输出
cout<<endl;
}
}
else
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++) cout<<ans[(i-1)*m+j]<<" ";
cout<<endl;
}
}
system("pause");
return 0;
}