国庆 day 2 上午
阅读原文时间:2023年07月14日阅读:1

一道图论神题(god)

Time Limit:1000ms   Memory Limit:128MB

题目描述

LYK有一张无向图G={V,E},这张无向图有n个点m条边组成。并且这是一张带权图,只有点权。

LYK想把这个图删干净,它的方法是这样的。每次选择一个点,将它删掉,但删这个点是需要代价的。假设与这个点相连的还没被删掉的点是u1,u2,…,uk。LYK将会增加a[u1],a[u2],…,a[uk]的疲劳值。

它想将所有点都删掉,并且删完后自己的疲劳值之和最小。你能帮帮它吗?

输入格式(god.in)

第一行两个数n,m表示一张n个点m条边的图。

第二行n个数ai表示点权。

接下来m行每行三个数u,v,表示有一条连接u,v的边。数据保证任意两个点之间最多一条边相连,并且不存在自环。

输出格式(god.out)

你需要输出这个最小疲劳值是多少。

输入样例

4 3

10 20 30 40

1 4

1 2

2 3

输出样例

40

样例解释

一个合理的方法是先删4号点,此时有10点疲劳值。接下来删3号点,获得20点疲劳值,再删2号点,获得10点疲劳值,最后删1号点,没有疲劳值。总计40点疲劳值。

对于30%的数据n<=10。

对于60%的数据n,m<=1000。

对于100%的数据1<=n,m,ai<=100000

思路:贪心,一遍AC。

#include
#include
#include
#include
#include
#define MAXN 101000
using namespace std;
mapma[MAXN];
int n,m,tot,cap[MAXN];
long long ans;
int to[MAXN*],net[MAXN*],head[MAXN*];
struct nond{
int val,id;
}v[MAXN];
int cmp(nond a,nond b){
return a.val>b.val;
}
void add(int u,int v){
to[++tot]=v;net[tot]=head[u];head[u]=tot;
to[++tot]=u;net[tot]=head[v];head[v]=tot;
}
int main(){
freopen("god.in","r",stdin);
freopen("god.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%d",&v[i].val);
v[i].id=i;
cap[i]=v[i].val;
}
sort(v+,v++n,cmp);
for(int i=;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
ma[u][v]=;
ma[v][u]=;
}
for(int i=;i<=n;i++){
for(int j=head[v[i].id];j;j=net[j])
if(ma[v[i].id].find(to[j])!=ma[v[i].id].end()){
ans+=cap[to[j]];
ma[to[j]].erase(v[i].id);
}
}
cout<<ans;
}
/*
4 3
10 20 30 40
1 4
1 2
2 3
*/

位运算2(bit)

Time Limit:1000ms   Memory Limit:128MB

题目描述

LYK拥有一个十进制的数N。它赋予了N一个新的意义:不考虑N的符号,将N每一位都拆开来后再加起来就是N所拥有的价值。例如数字123拥有6的价值,数字999拥有27的价值,数字-233拥有8的价值。

假设数字N的价值是K,LYK想找到一个价值是K+1的数字,当然这个答案实在太多了,LYK想使得这个价值为K+1的数字尽可能大,并且需要保证这个数字小于N。

输入格式(bit.in)

一个整数N。

输出格式(bit.out)

一个数表示答案。你需要输出一个整数,且这个数不包含前导0。

输入样例1

199

输出样例1

-299

输入样例2

1520

输出样例2

1512

对于20%的数据|N|<=10

对于40%的数据|N|<=100

对于60%的数据|N|<=10^9

对于80%的数据|N|<=10^1000

对于100%的数据|N|<=10^100000。

思路:大模拟,有三种情况没有考虑,挂掉了3个点。

~~(>_<)~~ 改不过来了90分代码:

#include
#include
#include
#include
using namespace std;
char n[];
int len;
int num[],tmp[],ans[];
int main(){
freopen("bit.in","r",stdin);
freopen("bit.out","w",stdout);
int cnt=,flag=,flag1=;
scanf("%s",n);
len=strlen(n);
if(n[]!='-'){
for(int i=;i=){
int y=;
num[len-]-=;num[len]+=;
while(num[y]==) y++;
for(int i=y;i<=len;i++) cout<&&num[len-]>=){
if(num[len-]!=){
int bns=-num[len-]-,y=;
num[len-]-=;
num[len-]=;
num[len]-=bns;
while(num[y]==) y++;
for(int i=y;i<=len;i++) ans[++cnt]=num[i]; } else{ int x=,y=; int bns=-num[len-]-; while(!num[len-x]&&len!=) x++; num[len-x]-=; num[len-x+]=; num[len-x+]=num[len]-bns; while(num[y]==) y++; for(int i=y;i<=len-x+;i++) ans[++cnt]=num[i]; for(int i=len-x+;i<=len;i++) ans[++cnt]=; } for(int i=;i<=cnt;i++) if(ans[i]>=||ans[i]<) flag=; if(len==cnt) for(int i=;i<=cnt;i++) if(ans[i]>tmp[i]){
flag=;
break;
}
else if(ans[i]=;i--)
if(tmp[i]!=){
flag1=;
tmp[i]+=;
break;
}
if(!flag1) tmp[]+=;
}
if(!flag) for(int i=;i<=len;i++) cout<=;i--)
if(tmp[i]!=){
flag1=;
tmp[i]+=;
break;
}
if(!flag1) tmp[]+=;
cout<<"-";
if(flag1) for(int i=;i<=len;i++) cout<<tmp[i];
else for(int i=;i<=len;i++) cout<<tmp[i];
return ;
}
}

这是标程:

#include
#include
#include
#include
#include
#include
#include
using namespace std;
char s[];
int len,sum,i,j,FLAG;
int main()
{
freopen("bit.in","r",stdin);
freopen("bit.out","w",stdout);
scanf("%s",s); FLAG=;
if (s[]!='-')
{
len=strlen(s); sum=;
for (i=len-; i>=; i--)
{
if (s[i]>='' && sum>=)
{
s[i]=char(s[i]-);
sum=;
for (j=i+; j=) {s[j]=''; SS-=;} else {s[j]=char(SS+''); SS=;}
}
FLAG=;
break;
}
sum+=''-s[i];
}
if (FLAG)
{
for (i=; i=; i--)
{
if (s[i]<'')
{
s[i]=char(s[i]+);
FLAG=;
break;
}
}
if (FLAG)
{
for (i=; i<len; i++) if (s[i]!='') break;
for (j=min(len-,i); j<len; j++) cout<<s[j]; cout<<endl;
return ;
}
cout<<;
for (i=; i<len; i++) if (s[i]!='') break;
for (j=min(len-,i); j<len; j++) cout<<s[j]; cout<<endl;
return ;
}

    FLAG=;  
    cout<<'-';  
    len=strlen(s);  
    for (i=len-; i>=; i--)  
    {  
        if (s\[i\]<'')  
        {  
            s\[i\]=char(s\[i\]+);  
            FLAG=;  
            break;  
        }  
    }  
    if (FLAG)  
    {  
        for (i=; i<len; i++) if (s\[i\]!='') break;  
        for (j=min(len-,i); j<len; j++) cout<<s\[j\]; cout<<endl;  
        return ;  
    }  
    cout<<;  
        for (i=; i<len; i++) if (s\[i\]!='') break;  
        for (j=min(len-,i); j<len; j++) cout<<s\[j\]; cout<<endl;  
return ;  

}

逆序对(pair)

Time Limit:1000ms   Memory Limit:128MB

题目描述

LYK最近在研究逆序对。

这个问题是这样的。

一开始LYK有一个2^n长度的数组ai。

LYK有Q次操作,每次操作都有一个参数k。表示每连续2^k长度作为一个小组。假设n=4,k=2,则a[1],a[2],a[3],a[4]为一个小组,a[5],a[6],a[7],a[8]为一个小组,a[9],a[10],a[11],a[12]为一个小组,a[13],a[14],a[15],a[16]也为一个小组。

然后LYK对于每个小组都翻转,也就是说原数组会变成a[4],a[3],a[2],a[1],a[8],a[7],a[6],a[5],a[12],a[11],a[10],a[9],a[16],a[15],a[14],a[13]。之后它想求出这2^n个数的逆序对是多少。

因此你需要输出对于每次操作,操作完后这2^n个数的逆序对有多少对。

两个数ai,aj被称为逆序对当且仅当iaj。

输入格式(pair.in)

第一行一个数n。

接下来一行2^n个数ai表示一开始的数组。

接下来一行一个数Q,表示操作的次数。

接下来一行Q个数,表示每次操作的参数k。

输出格式(pair.out)

Q行,表示每次操作后的答案。

输入样例

2

2 1 4 3

4

1 2 0 2

输出样例

0

6

6

0

样例解释

第一次操作,{2,1,4,3}->{1,2,3,4}

第二次操作,{1,2,3,4}->{4,3,2,1}

第三次操作,{4,3,2,1}->{4,3,2,1}

第四次操作,{4,3,2,1}->{1,2,3,4}

对于30%的数据n<=10,Q<=10。

对于50%的数据n<=10,Q<=1000。

对于80%的数据n<=10,Q<=200000。

对于100%的数据n<=17,Q<=200000,1<=ai<=2^n。

思路:

例:1 2 3 4 5 6 7 8  k=3

翻转结果为 8 7 6 5 4 3 2 1

翻转过程可以拆分为:

  第一步: 2 1 4 3 6 5 8 7

  第二步:4 3 2 1 8 7 6 5

  第三步:8 7 6 5 4 3 2 1

所以每一次翻转都可以拆分,而拆分的过程就是交换相邻的2^(步数-1)

所以归并排序求逆序对的时候,用st表维护从第i个位置,长为2^j的区间的逆序对个数

然后求出对应区间的顺序对个数

维护所有长为2^i的区间的逆序对个数rev[i]和顺序对个数pos[i]

统计答案的时候,拆分就是swap(rev,pos)

在这里顺序对个数是用总个数-逆序对个数求的

所以就会有一个问题,例:

2 2 3 3

实际上的:

pos : 0  0

rev :  0  0

但求的时候,pos :2  4

因为用总个数-逆序对个数=顺序对个数+相等的数对

所以归并过程中,还要预处理从第i个位置,长为2^j的区间的相等数对个数

计算rev,pos时,同时计算same[i],表示所有长为2^i的区间相等的数对个数

求解的时候 先swap(rev,pos),然后rev-=same,再 pos=总的-rev

#include
#include
#include
#include
using namespace std;
const int N=(<<)+; int n,T,PP,now,A,sum,o; int p[N],a[N],b[N],RR[N]; long long t[],TT[]; long long st[N][],ST[N][]; void pre(int l,int r){ if(l==r) return; int mid=(l+r)/; pre(l,mid); pre(mid+,r); int i=l,j=mid+,o=l; for(i=l;i<=r;i++) b[i]=a[i]; for(i=r;i>=mid;i--){
RR[i]=i;
if(i!=r&&b[i+]==b[i])
RR[i]=RR[i+];
}
i=l;j=mid+;
while(i<=mid&&j<=r){
if(b[i]<=b[j]){
a[o++]=b[i];
if(b[i]==b[j])
ST[l][p[r-l+]]+=RR[j]-j+;
i++;
}
else{
a[o++]=b[j];
st[l][p[r-l+]]+=mid-i+;
j++;
}
}
if(i<=mid)
for(j=i;j<=mid;j++) a[o++]=b[j];
else if(j<=r)
for(i=j;i<=r;i++) a[o++]=b[i];
}
int main(){
freopen("pair.in","r",stdin);
freopen("pair.out","w",stdout);
scanf("%d",&n);
sum=(<<n);
for(int i=;i<=sum;i++) scanf("%d",&a[i]);
for(int i=;i<=;i++) p[<<i]=i;
pre(,sum);
for(int i=;i<=n;i++)
for(int j=;j<=(<<n);j+=(<<i)){
t[i]+=st[j][i];
TT[i]+=ST[j][i];
}
scanf("%d",&T);
while(T--){
long long ans=;int Q;
scanf("%d",&Q);
for(int i=;i<=Q;i++) t[i]=1ll*(<<i-)*(<<i-)*(<<n-i)-TT[i]-t[i];
for(int i=;i<=n;i++) ans+=t[i];
printf("%I64d\n",ans);
}
return ;
}