1. 程式人生 > >8.8聯考題解

8.8聯考題解

對象 hide 最小 con 序列 none 出現 sin clas

今天的T1讓我懷疑我是不是在做奧賽題……這考的是什麽知識點啊這個,會不會用絕對值函數?

Evensgn 的債務

時間限制: 1 Sec 內存限制: 128 MB

題目描述

Evensgn 有一群好朋友,他們經常互相借錢。假如說有三個好朋友A,B,C。A 欠 B 20 元,B 欠 C 20 元,總債務規模為 20+20=40 元。Evensgn 是個追求簡約的人,他覺得這樣的債務太繁雜了。他認為,上面的債務可以完全等價為 A 欠C20 元,B 既不欠別人,別人也不欠他。這樣總債務規模就壓縮到了 20 元。現在給定 n 個人和 m 條債務關系。Evensgn 想找到一種新的債務方案,使得每個人欠錢的總數不變,或被欠錢的總數不變(但是對象可以發生變化),並且使

得總債務規模最小。

輸入

輸入文件第一行兩個數字 n; m,含義如題目所述。

接下來 m 行,每行三個數字 ai; bi; ci,表示 ai 欠 bi 的錢數為 ci。

註意,數據中關於某兩個人 A 和 B 的債務信息可能出現多次,將其累加即可。

如”A 欠 B 20 元”、”A 欠 B 30 元”、”B 欠 A 10 元”,其等價為”A 欠 B 40 元”。

輸出

輸出文件共一行,輸出最小的總債務規模。

樣例輸入輸出

樣例輸入 1
5 3
1 2 10
2 3 1
2 4 1
樣例輸入 2
4 3
1 2 1
2 3 1
3 1 1

樣例輸出

樣例輸出 1
10
樣例輸出 2
0

數據範圍

對於 30% 的數據,1 ≤ n ≤ 10,1 ≤ m ≤ 10。

對於 60% 的數據,1 ≤ n ≤ 100, 1 ≤ m ≤ 104。

對於 80% 的數據,1 ≤ n ≤ 104,1 ≤ m ≤ 104。

對於 100% 的數據,1 ≤ n ≤ 106,1 ≤ m ≤ 106。

對於所有的數據,保證 1 ≤ ai; bi ≤ n; 0 < ci ≤ 100。

題解

不想寫題解……剛開始覺得,這是絕對值之和/2吧?弄了個環的例子繞一繞,好像還真沒什麽問題……後來閑得慌自己證明,發現用負數表示borrow,正數表示lend,正負數一消取絕對值正好是債務規模,似乎挺有道理?交題之前又有些猶豫,這題是考的什麽算法啊?還是交了,居然A了……A掉這道題,你需要掌握:循環語句、開數組、輸入輸出、調庫、絕對值函數(不會用cmath庫用if語句判斷取相反數也行),可以給下一屆高一當第一次考試題了;或許文化課一調的語句填空也行。昨天的第一題,雖然是原題好歹是題,還用了MP算法,但是這個,完全不懂意義何在。

技術分享
include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int sj=1000010;
int a[sj]={0},n,m,a1,a2,a3,ans=0;
int r()
{
    int jg=0,jk;
    jk=getchar()-0;
    if(jk<=9&&jk>0) jg=jk;
    jk=getchar()-0;
    while(jk>=0&&jk<=9)
    {
       jg=jg*10+jk;
       jk=getchar()-0;
    }
    return jg;
}
int main()
{
    //freopen("t1.txt","r",stdin);
    n=r();
    m=r();
    for(int i=1;i<=m;i++) 
    {
       a1=r();
       a2=r();
       a3=r();
       a[a1]-=a3;
       a[a2]+=a3;
    }
    for(int i=1;i<=n;i++)  ans+=abs(a[i]);
    ans/=2;
    printf("%d",ans);
    //while(1);
    return 0;
}
debt

Number

時間限制: 1 Sec 內存限制: 256 MB

題目描述

一個排列,求出了 a 數組,其中 ai 表示第 i 個數左邊有多少個數比它小

計算出原來的排列

輸入

第一行輸入 n 接下來 n - 1 個整數 ai,下標從 2 開始

輸出

輸出 n 個整數表示原排列

樣例輸入

5
1
2
1
0

樣例輸出

2
4
5
3
1

提示

對於 20% 的數據滿足:1 ≤ n ≤ 10

對於 50% 的數據滿足:1 ≤ n ≤ 1000

對於 100% 的數據滿足,1 ≤ n ≤ 100000

保證解存在

題解

這道題還是比較有意思的。剛開始啥都想不出來,先打了個dfs,又弄了個出數據的程序(走向對拍?)。發現dfs只能跑20分,有點難過。第三題看起來很不友好,第二題還拿不了分這是要完啊……然後寫寫畫畫,猛然發現這個最後一個好像是確定的,那倒數第二個呢,倒數第三個呢……這不就想出來了嗎,記錄一下每個數的temp(左邊可能有幾個比它小的),選出來一個就把比它大的都--,每次查哪一個數正好符合要求就行了。需要快速查詢,一開始想的是鏈表。打了個(n^2+2^n)/2的鏈表版本,出不了樣例,又轉去打n^2的,發現好像先選大的和先選小的還不一樣。感性地論證一下,較小數的temp更不容易變小,所以盡量先選小的。心懷疑慮地把鏈表版本改對,和1000的數據拍了一下發現能過,也就放心了。其實這個效率跑個10000也是綽綽有余的,但是出題人說給50就給50多一點分都沒有QAQ。第三題想不出來再回來看,要把n^2降下來,查詢得放在樹上,恍然大悟——原來是個平衡樹題啊。但是沒想到可以用有旋Treap查詢和刪除,被50分版本限制住了思路一直想著Splay或者無旋Treap的區間操作,Splay極容易打掛無旋Treap沒打過區間build函數不會寫,還是算了(早知道有旋Treap也可以做就打正解了嘛)。下午改題歡樂地拿有旋Treap輕松水過,delete函數寫錯一個字母又查了半天錯。

技術分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
const int sj=100010;
int n,a[sj],ans[sj],size,root;
struct Treap
{
     int v,l,r,key,size;
}t[sj];
void update(int x)
{
     t[x].size=t[t[x].l].size+t[t[x].r].size+1;
}
void rturn(int &x)
{
     int tt=t[x].l;
     t[x].l=t[tt].r;
     t[tt].r=x;
     t[tt].size=t[x].size;
     update(x);
     x=tt;
}
void lturn(int &x)
{
     int tt=t[x].r;
     t[x].r=t[tt].l;
     t[tt].l=x;
     t[tt].size=t[x].size;
     update(x);
     x=tt;
}
void cr(int &x,int y)
{
     if(x==0)
     {
        size++;
        x=size;
        t[x].size=1;
        t[x].v=y;
        t[x].key=rand();
        return;
     }
     t[x].size++;
     if(y>t[x].v)
     {
         cr(t[x].r,y);
         if(t[t[x].r].key<t[x].key)  lturn(x);
     }
     else
     {
         cr(t[x].l,y);
         if(t[t[x].l].key<t[x].key)  rturn(x);
     }
}
int query(int x,int y)
{
    if(x==0) return 0;
    if(t[t[x].l].size>=y) return query(t[x].l,y);
    if(t[t[x].l].size+1<y) return query(t[x].r,y-t[t[x].l].size-1);
    return t[x].v;
}
void sc(int &x,int y)
{
     if(x==0) return;
     if(t[x].v==y)
     {
       if(t[x].l*t[x].r==0) x=t[x].l+t[x].r;
       else if(t[t[x].l].key<t[t[x].r].key)
         rturn(x),sc(x,y);
       else  
         lturn(x),sc(x,y);
     }
     else if(y>t[x].v)
        t[x].size--,sc(t[x].r,y);
     else    t[x].size--,sc(t[x].l,y);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)  cr(root,i);
    for(int i=2;i<=n;i++)  scanf("%d",&a[i]);
    ans[n]=a[n]+1;
    sc(root,ans[n]);
    for(int i=n-1;i>1;i--)
    {
       ans[i]=query(root,a[i]+1);
       sc(root,ans[i]); 
    }
    ans[1]=t[root].v;
    for(int i=1;i<=n;i++)
       printf("%d\n",ans[i]);
    return 0;
}
number

與非

時間限制: 2 Sec 內存限制: 256 MB

題目描述

作為一名新世紀共產主義的接班人,你認識到了資本主義的軟弱性與妥協性,決定全面根除資本主義,跑步邁入共產主義。但是當你即將跨入共產主義大門的時候,遇到了萬惡的資本家留下的與非電路封印,經過千辛萬苦的研究,你終於把復雜的破解轉變成了以下問題:

初始時你有一個空序列,之後有N個操作。

操作分為一下兩種:

1 x:在序列末尾插入一個元素x(x=0或1)。

2 L R:定義nand[L,R]為序列第L個元素到第R個元素的與非和,詢問nand[L,L]^nand[L,L+1]^nand[L,L+2]^......^nand[L,R]。

Nand就是先與,再取反

輸入

從文件nand.in中讀入數據。

輸入第一行一個正整數N,表示操作個數。

接下來N行表示N個操作。

為了體現程序的在線性,記lastans為上一次操作二的回答,初始lastans=0,。對於操作1,你需要對x異或lastans。對於操作二,設現在序列中的元素個數為M,如果lastans=1,那麽你需要作如下操作:L=M-L+1,R=M-R+1,swap(L,R)

輸出

輸出到nand.out中。

輸出有多行。為對於每一個操作二的回答。

樣例輸入

6
1 1
1 1
1 0
2 1 2
2 1 3
2 2 3

樣例輸出

1
0
0

提示

【數據規模和約定】

數據點 N的規模 操作一的個數M1 操作二的個數M2

1 N<=1000 M1<=500 M2<=500

2 N<=1000 M1<=500 M2<=500

3 N<=200000 M1<=100000 M2<=100000

4 N<=200000 M1<=100000 M2<=100000

5 N<=1000000 M1<=900000 M2<=100000

6 N<=4000000 M1<=3900000 M2<=100000

7 N<=4000000 M1<=3900000 M2<=100000

8 N<=4000000 M1<=3900000 M2<=100000

9 N<=4000000 M1<=3900000 M2<=100000

10 N<=4000000 M1<=3900000 M2<=100000

對於所有數據,滿足N<=4000000,M1<=3900000,M2<=100000,x={0,1},L<=R。

由於輸入規模較大,請使用較快的讀入方式以防讀入超時。

題解

%%%超哥。我承認我確實沒讀懂題,所以連20的暴力分都沒拿到。連續兩天T3爆零,很憂傷啊。超哥的思路完勝std!

f[i]=!(f[i-1]&a[i])
nand[i,j]=!(nand[i,j-1]&a[j])=!(……!(!(!(a[i]&a[i+1]))&a[i+2])……&a[j])
f[j]=!(f[j-1]&a[j])=!(……!(!(!(f[i-1]&a[i])&a[i+1]))&a[i+2])……&a[j])
唯一的差別在中間部分,真正的nand[i,j]是a[i]&a[i+1],而f[j]是(!(f[i-1]&a[i]))&a[i+1]
a[i+1]=0時前後都是0,一定相等
a[i+1]=1時若a[i]=0,前為0,後為1
若a[i]=1且f[i-1]=1,前為1,後為0
若a[i]=1但f[i-1]=0,前後還是相等的

對於每一個詢問的區間,從它有一位為0開始就可以套根據f數組計算出的前綴和sum了~至於前面的,暴力算吧。

其實也是想了想前綴和區間轉化之類的思路的,但是沒想出來……改題的時候想著亦或有可減性(亦或兩次變成1),本蒟蒻傻傻地打了sum[r]-sum[l-1]然後成功出了負數……最後批判這個題面一句:名字叫與非,題幹裏說先與再取反,雖然說大概能理解可是畢竟二進制還有個~運算叫取反啊……

技術分享
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int sj=3900005;
int r()
{
    int jg=0,jk;
    jk=getchar()-0;
    if(jk<=9&&jk>0) jg=jk;
    jk=getchar()-0;
    while(jk>=0&&jk<=9)
    {
       jg=jg*10+jk;
       jk=getchar()-0;
    }
    return jg;
}
int n,a1,a2,op,s[sj],lans,ge,t,jg,f[sj],sum[sj],na[sj];
int main()
{
    n=r();
    sum[0]=1;
    for(int l=1;l<=n;l++)
    {
        op=r();
        a1=r();
        if(op==1)
        {
           a1^=lans;
           ge++;
           s[ge]=a1;
           f[ge]=!(f[ge-1]&s[ge]);
           sum[ge]=sum[ge-1]^f[ge];
           if(ge==1)  f[1]=s[1],sum[1]=s[1];
           
        }
        if(op==2)
        {
           a2=r();
           if(lans==1)
           {
              a1=ge-a1+1;
              a2=ge-a2+1;
              t=a1;
              a1=a2;
              a2=t;
           }
           jg=na[a1]=s[a1];
           for(int j=a1+1;j<=a2;j++)
           {
             if(s[j]==1)
             {
               na[j]=!(na[j-1]&s[j]);
               jg^=na[j];
             }
             else
             {
                jg^=sum[a2];
                jg^=sum[j-1];
                break;
             }
           }
           lans=jg;
           printf("%d\n",jg);
        }
    }
    return 0;
}
nand

8.8聯考題解