版权声明:https://github.com/godspeedcurry 欢迎加好友哦 https://blog.csdn.net/qq_38677814/article/details/82056327
吐槽一下网管中心,爬山去了没人看守?当这个比赛是啥?虽说是周末也不该这样子吧
本次比赛主要负责看题,人有点难过也做不出题系列(
按照签到顺序写题解
第一道费马大定理裸题 n>2无解
思路:n这么大暴算肯定不行呀,只有构造了~
构造思路队友想的很巧妙
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n,a;
scanf("%d %d",&n,&a);
if(n==0){
printf("-1 -1\n");
}
else if(n==1){
printf("%d %d\n",1,a+1 );
}
else if(n==2){
if(a&1){
printf("%d %d\n",(a*a-1)>>1,(a*a+1)>>1 );//奇数的构造
}
else if((a&(a-1))==0){//2的次方 可以通过 4,3,5构造
printf("%d %d\n",a/4*3,a/4*5);
}
else{//其它偶数情况 比如 6 我们把它变成3 即6 8 10 -> 3 4 5这些是等价的
int u;
for(int i=1;;i++){
if((a>>i)&1){
u=i;
break;
}
}
int temp=a>>u;
printf("%d %d\n",((temp*temp-1)>>1)<<u, ((temp*temp+1)>>1)<<u);
}
}
else{
printf("-1 -1\n");
}
}
return 0;
}
1009
题意:给你一棵树,每个边有边权,统计所有排列下(全排列)的路径和
思路:N很大 不可能枚举全排列,转化成计算贡献
什么时候会通过1条边,左边到右边,右边到左边
假设左边的大小为L,右边为R 显然L+R=n
N!种全排列中经过该边的次数有多少呢
L*R个点集对
2*L*R个排列方式
对于每一点对,其他点对排列方式(N-2)!
捆绑插空 空位N-1个
sum= ∑cost_of_edge*(2*L*R)*(N-1)!
阶乘可以预处理
左右两边的大小可以通过一次dfs得到
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define MOD (int)(1e9+7)
#define mp(a,b) make_pair(a,b)
inline void rd(int &x){
scanf("%d",&x);
}
struct edge{
int x,y,l;
}e[100200];
int siz[102000];
int vis[102000];
vector<pair<int,int> > G[102000];
void dfs(int x){
siz[x]=1;
for(int i=0;i<G[x].size();i++){
int to=G[x][i].first;
if(!vis[to]){
vis[to]=1;
dfs(to);
vis[to]=0;
siz[x]+=siz[to];
}
}
}
ll fac[102000];
void init(){
fac[0]=1;
for(int i=1;i<=101000;i++){
fac[i]=(fac[i-1]*i)%MOD;
}
}
int main(int argc, char const *argv[])
{
init();
int n,x,y,l;
while(~scanf("%d",&n)){
memset(siz,0,sizeof(siz));
for(int i=1;i<=n;i++){
G[i].clear();
}
for(int i=1;i<n;i++){
rd(x);rd(y);rd(l);
e[i].x=x;
e[i].y=y;
e[i].l=l;
G[x].push_back(mp(y,l));
G[y].push_back(mp(x,l));
}
vis[1]=1;
dfs(1);
ll ans=0;
for(int i=1;i<n;i++){
int Min=min(siz[e[i].x],siz[e[i].y]);
int Ltree=Min;
int Rtree=n-Min;
ans+=2ll*e[i].l*Ltree*Rtree%MOD*fac[n-1]%MOD;
ans%=MOD;
}
printf("%lld\n",ans );
}
return 0;
}
1010
dp+数据结构维护(树状数组或者线段树)
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&(-x))
struct node{
int x,y,v;
}p[100200];
int dp[100200],num[100200],a[100200],t;
bool cmp(node a,node b){
return a.x!=b.x?a.x<b.x:a.y<b.y;
}
void init(){
memset(dp,0,sizeof dp);
memset(num,0,sizeof num);
}
void update(int x,int val){
for(int i=x;i<=t;i+=lowbit(i)){
num[i]=max(num[i],val);
}
}
int query(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)){
res=max(res,num[i]);
}
return res;
}
int main(){
int T;scanf("%d",&T);
int n;
while(T--){
init();
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].v);
}
for(int i=1;i<=n;i++) a[i]=p[i].y;
sort(a+1,a+1+n);
t=unique(a+1,a+1+n)-a-1;//离散化 去重
for(int i=1;i<=n;i++){
p[i].y=lower_bound(a+1,a+1+t,p[i].y)-a;//重新赋值
}
sort(p+1,p+1+n,cmp);
int ans=0;
int now=1;
for(int i=1;i<=n;i++){
dp[i]=p[i].v;
}
//树状数组维护了区间最大值
for(int i=1;i<=n;i++){
while(now<i&&p[now].x<p[i].x){
update(p[now].y,dp[now]);//用dp[now]更新
++now;
}
dp[i]=p[i].v+query(p[i].y-1);
ans=max(ans,dp[i]);
}
printf("%d\n",ans );
}
return 0;
}
1003
费马小定理:
a^(p-1)≡1(mod p)p为质数
∴(a+b)^p=(a+b)^(p-1)*(a+b)=a+b(mod p)
a^p+b^p=a*a^(p-1)+b*b^(p-1)(mod p)=a+b(mod p)
还有一个限制条件:
看的时候一脸懵逼,这是什么鬼
手算找了几个小的好像是对的?
引入定义-原根
#include <bits/stdc++.h>
using namespace std;
int main(){
int T;cin>>T;
while(T--){
int p;cin>>p;
for(int i=1;i<=p;i++){
for(int j=1;j<=p;j++){
printf("%d%c",((i-1)%p+(j-1)%p)%p,j==p?'\n':' ');
}
}
for(int i=1;i<=p;i++){
for(int j=1;j<=p;j++){
printf("%d%c",((i-1)%p*(j-1)%p)%p,j==p?'\n':' ');
}
}
}
return 0;
}
1001
优先队列+贪心
有弹性的优先队列,即可以反悔
每次取出价格比当前城市低的,然后卖掉,得到差价
可能后面的城市还有出价更高的,那我们需要把之前的退掉,然后再次得到差价
可以用map记录一下卖出的东西
#include <bits/stdc++.h>
using namespace std;
#define ll long long
map<int,int> sell;
priority_queue<ll,vector<ll>,greater<ll> > PQ;
ll a[100202];
void init(){
while(!PQ.empty()){
PQ.pop();
}
sell.clear();
}
int main(){
int T;cin>>T;
while(T--){
int n;cin>>n;
for(int i=1;i<=n;i++) scanf("%I64d",a+i);
ll ans=0;
int cnt=0;
init();
//第一个城市会把物品放进优先队列
//以后的城市会取出最小的物品,然后如果当地价格高于他则卖掉
//买卖次数+1
//如果那个物品曾经卖出过 不卖,然后修改次数
for(int i=1;i<=n;i++){
if(!PQ.empty()&&PQ.top()<a[i]){
ll Min=PQ.top();PQ.pop();
ans+=a[i]-Min;
cnt++;
auto It=sell.find(Min);
if(It!=sell.end()){
cnt--;
if(--sell[Min]==0){
sell.erase(It);
}
}
sell[a[i]]++;
PQ.push(a[i]);
}
PQ.push(a[i]);
}
printf("%I64d %d\n",ans,cnt*2);
}
return 0;
}
1007
循环节很容易想到,难点在于一些细节的处理
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+100;
ll n,m,s,k;
ll a[maxn];
ll pre[maxn<<1];
ll seg[maxn<<2];
inline ll ls(ll x){
return x<<1;
}
inline ll rs(ll x){
return x<<1|1;
}
inline void push_up(ll rt){
seg[rt]=min(seg[ls(rt)],seg[rs(rt)]);
}
void build(ll l,ll r,ll rt){
if(l==r){
seg[rt]=pre[l];
return;
}
ll mid=(l+r)>>1;
build(l,mid,ls(rt));
build(mid+1,r,rs(rt));
push_up(rt);
}
ll query(ll L,ll R,ll l,ll r,ll rt){
ll res=1ll<<60;
if(L<=l&&r<=R){
return seg[rt];
}
ll mid=(l+r)>>1;
if(L<=mid) res=min(res,query(L,R,l,mid,ls(rt)));
if(R>mid) res=min(res,query(L,R,mid+1,r,rs(rt)));
push_up(rt);
return res;
}
int main(){
int T;cin>>T;
for(int c=1;c<=T;c++){
scanf("%I64d%I64d%I64d%I64d",&n,&s,&m,&k);
memset(pre,0,sizeof(pre));
for(ll i=0;i<n;i++){
scanf("%I64d",a+i);
}
ll ans=0;
ll num=__gcd(n,k);
ll len=n/num;
for(int i=1;i<=num;i++){
int index=i-1;
for(int j=1;j<=len;j++){
pre[j]=pre[j+len]=a[index];
index=(index+k)%n;
}
for(int j=1;j<=len*2;++j){
pre[j]+=pre[j-1];
}
build(1,len<<1,1);
ll ans1=0;
ll res=0;
for(int j=len+1;j<=len*2;j++){
res=max(res,pre[j]-query(j-(m%len),j,1,len<<1,1));
}
if(m<len){
ans=max(res,ans);
continue;
}
if(m%len)ans1+=res;
if(pre[len]>0){
ans1+=(m)/len*pre[len];
}
ll ans2=0;
for(int j=len+1;j<=len*2;j++){
ans2=max(ans2,pre[j]-query(j-len,j,1,len<<1,1));
}
if(pre[len]>0) ans2+=(m-len)/len*pre[len];//牺牲一个循环节去得到上面的那个答案
ans=max(ans,max(ans1,ans2));
}
printf("Case #%d: %I64d\n",c,max(0ll,s-ans) );
}
return 0;
}